///////////////////////////////////////////////////// // // // InitiateScanThread() // // // ///////////////////////////////////////////////////// //Description: Kicks of a scan of the host in a separate // thread. ServiceMain() will wait for // this thread to complete. This function // is only called in Enterprise mode. // //Returns: void ////////////////////////////////////////////////////// internal void InitiateScanThread() { AgentScanner scanner = new AgentScanner(); CwXML.CodewordAgentAnomalyReport anomalyReport = new CwXML.CodewordAgentAnomalyReport(); //this wont modify our global variable "ScanResultsLog", so we have to write it to a file //in this child thread and then once back in the main thread, slurp it back up try { ScanResultsLog = scanner.StartScanTask(ref anomalyReport); StreamWriter sw = new StreamWriter("xxzz1tmp1"); sw.Write(ScanResultsLog.ToString()); sw.Close(); } catch (Exception ex) { StreamWriter sw = new StreamWriter("errcw.txt"); sw.WriteLine(ex.Message); if (ex.InnerException != null) sw.WriteLine(ex.InnerException.Message); sw.Close(); } return; }
///////////////////////////////////////////////////// // // // ServiceMain() // // // ///////////////////////////////////////////////////// //Description: Entry point for the agent service process. // This function is called automatically // by the Windows SCM if we are running // as a service, or it's called manually // in AgentMain if not a service. // // This function's prototype is dictated // by the Win32Helper.LPSERVICE_MAIN_FUNCTIONW // delegate definition. // //Returns: true if successful ///////////////////////////////////////////////////// internal unsafe void ServiceMain(uint dwNumServicesArgs, ref IntPtr lpServiceArgVectors) { AgentSettings = new Dictionary<string, string>(); AgentServiceLog = new StringBuilder(); ScanResultsLog = new StringBuilder(); //============================================= // INITIALIZATION //============================================= // //1. Load settings from XML file extracted to local dir from MSI // if (!LoadAgentSettings(ref AgentSettings)) return; //============================================= // SET SERVICE CONTROL HANDLER FUNCTION //============================================= //the function ServiceMain() is called either by: // (1) the agent binary itself inside CwAgent.exe in "Fire and Forget" mode // (2) the CwAgent service has been started by the SCM // //in #1, we dont need to do anything special, but in #2, we have to do a few items //to make sure the SCM is "in the know": // http://msdn.microsoft.com/en-us/library/ms685984(VS.85).aspx // //we will distinguish between case #1 and case #2 by the number of args if (dwNumServicesArgs > 0) { //get a pointer to our callback delegate. Win32Helper.LPHANDLER_FUNCTION lpHandlerProc = new Win32Helper.LPHANDLER_FUNCTION(ServiceHandler); //call RegisterServiceCtrlHandler() with this ptr. all SCM notifications will be handled by it. IntPtr svcStatusHandle = Win32Helper.RegisterServiceCtrlHandler(AgentSettings["AgentServiceName"], lpHandlerProc); if (svcStatusHandle == IntPtr.Zero) return; //!!!!!!!!!!!!!!!!!!!!!!!!!!! //!! MUI IMPORTANTE !! //!!!!!!!!!!!!!!!!!!!!!!!!!!! //we must save this handle for later updates to SCM globalHSvcHandle = svcStatusHandle; bool success = false; //set service to the START_PENDING state try { ServiceHelper.SetServiceStatus(globalHSvcHandle, Win32Helper.SERVICE_START_PENDING, ref success); } catch (Exception) { } } //============================================= // ESCALATE PRIVILEGES //============================================= //we must have debug privs to succeed. if (!AgentScanner.EnvironmentHelper.EscalatePrivileges()) { //set our service to the STOPPED state try { bool success = false; ServiceHelper.StopService(AgentSettings["AgentServiceName"]); ServiceHelper.SetServiceStatus(globalHSvcHandle, Win32Helper.SERVICE_STOPPED, ref success); } catch (Exception) { } return; } AgentServiceLog.AppendLine("*********************************************"); AgentServiceLog.AppendLine("Codeword Agent v" + Assembly.GetExecutingAssembly().GetName().Version); AgentServiceLog.AppendLine("*********************************************"); AgentServiceLog.AppendLine("Copyright © 2009, Sippy Development International"); AgentServiceLog.AppendLine("Author: sippy"); AgentServiceLog.AppendLine("Please contact [email protected] with questions."); AgentServiceLog.AppendLine("*********************************************"); AgentServiceLog.AppendLine(""); AgentServiceLog.AppendLine("*********************************************"); AgentServiceLog.AppendLine(" INITIALIZE "); AgentServiceLog.AppendLine("*********************************************"); AgentServiceLog.AppendLine(""); AgentServiceLog.AppendLine("INITIALIZE: Codeword starting on " + DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss")); AgentServiceLog.AppendLine("INITIALIZE: Loading settings..."); //============================================= // STARTUP //============================================= // //1. determine our startup mode. // string[] possibleStartupModes = new string[] { "StartupFireAndForgetMode", "StartupRemoteControlMode", "StartupEnterpriseMode" }; string AgentStartupMode = ""; foreach (string s in possibleStartupModes) if (AgentSettings.ContainsKey(s)) if (AgentSettings[s] == "True") AgentStartupMode = s; AgentServiceLog.AppendLine("INITIALIZE: Agent startup mode set to " + AgentStartupMode); // //2. start TCP server and listen for commands // if (AgentStartupMode == "StartupRemoteControlMode" || AgentStartupMode == "StartupEnterpriseMode") { SslTcpServer server = new SslTcpServer(); string certfile = "", encPwd = "", issuer = ""; bool authClientToServer = false; bool authServerToClient = false; bool strongAuth = false; int port = 1111; AgentServiceLog.AppendLine("STARTUP: Initializing TCP/SSL server..."); AgentServiceLog.AppendLine("STARTUP: Using settings:"); //------------------------------------ // LOAD TCP SERVER SETTINGS //------------------------------------ //extract certificate from internal PKCS-12 file if provided if (AgentSettings.ContainsKey("AgentPFXFile")) if (AgentSettings["AgentPFXFile"] != "") certfile = Path.GetFileName(AgentSettings["AgentPFXFile"]); //get encrypted password for PFX keystore if (AgentSettings.ContainsKey("AgentPFXPassword")) if (AgentSettings["AgentPFXPassword"] != "") encPwd = AgentSettings["AgentPFXPassword"]; //server port to listen on locally if (AgentSettings.ContainsKey("AgentListeningPort")) if (AgentSettings["AgentListeningPort"] != "") port = int.Parse(AgentSettings["AgentListeningPort"]); //authenticate client to server? if (AgentSettings.ContainsKey("AgentAuthenticateClientToServer")) if (AgentSettings["AgentAuthenticateClientToServer"] == "True") authClientToServer = true; //authenticate server to client? if (AgentSettings.ContainsKey("AgentAuthenticateServerToClient")) if (AgentSettings["AgentAuthenticateServerToClient"] == "True") authServerToClient = true; //required issuer of client certs if (AgentSettings.ContainsKey("AgentEnforceCertificateIssuer")) if (AgentSettings["AgentEnforceCertificateIssuer"] != "") issuer = AgentSettings["AgentEnforceCertificateIssuer"]; //force strong authentication if (AgentSettings.ContainsKey("AgentEnforceStrongAuthentication")) if (AgentSettings["AgentEnforceStrongAuthentication"] == "True") strongAuth = true; AgentServiceLog.AppendLine(" PFX file name: " + certfile); AgentServiceLog.AppendLine(" Listening on port: " + port.ToString()); AgentServiceLog.AppendLine(" Authenticate client to server: " + authClientToServer.ToString()); AgentServiceLog.AppendLine(" Authenticate server to client: " + authServerToClient.ToString()); AgentServiceLog.AppendLine(" Required issuer: " + issuer); AgentServiceLog.AppendLine(" Strong authentication required: " + strongAuth.ToString()); //set server fields server.PFXFileName = certfile; server.EncryptedPassword = encPwd; server.ServerPort = port; server.AuthenticateClientToServer = authClientToServer; server.AuthenticateServerToClient = authServerToClient; server.RequiredIssuer = issuer; server.RequireStrongAuthentication = strongAuth; //insure the certificate file exists if (!File.Exists(certfile)) { AgentServiceLog.AppendLine("Error: PFX certificate file '" + certfile + "' does not exist!"); return; } //------------------------------------ // RUN THE SCAN IF MODE IS // StartupEnterpriseMode //------------------------------------ if (AgentStartupMode == "StartupEnterpriseMode") { //kick it off in a new thread so it doesnt stall the service //and cause the SCM to barf. Thread thr = new Thread(new ThreadStart(InitiateScanThread)); thr.Start(); while (!thr.IsAlive) { } Thread.Sleep(1); //we will wait for it to complete, b/c we've already set the status of //our service to RUNNING, so SCM is satisfied. //Ideally, we would also kick the RunServer() below in a new thread //as well, and synchronize the three threads. thr.Join(); } //read the data back in from the file the child thread just wrote //ScanResultsLog = new StringBuilder(File.ReadAllText("xxzz1tmp1")); //promptly delete the file //File.Delete("xxzz1tmp1"); //set our service to the RUNNING state try { bool success = false; ServiceHelper.SetServiceStatus(globalHSvcHandle, Win32Helper.SERVICE_RUNNING, ref success); } catch (Exception) { } //------------------------------------ // START THE TCP SERVER //------------------------------------ //pass the results of an enterprise mode scan, if there is one //note: ScanResultsLog is populated from the child thread above. try { server.RunServer(ScanResultsLog); } catch (Exception ex) { StreamWriter sw = new StreamWriter("SslServerError.txt", true); sw.WriteLine(ex.Message); sw.Close(); } //set our service to the STOPPED state try { bool success = false; ServiceHelper.StopService(AgentSettings["AgentServiceName"]); ServiceHelper.SetServiceStatus(globalHSvcHandle, Win32Helper.SERVICE_STOPPED, ref success); } catch (Exception) { } } //StartupFireAndForgetMode - do not start any server; just run the scan and report //note: if we get here, we are not being called by SCM. else if (AgentStartupMode == "StartupFireAndForgetMode") { AgentScanner scanner = new AgentScanner(); scanner.FireAndForget(); } return; }
///////////////////////////////////////////////////// // // // ExecuteCommand() // // // ///////////////////////////////////////////////////// //Description: Executes the given command and returns // a response object. // //Returns: A response code, one of the following: // RESPONSE_EXITING - connection closing // RESPONSE_OK - command completed successfully // RESPONSE_FAIL = command failed ///////////////////////////////////////////////////// internal CwXML.CodewordAgentResponse ExecuteCommand(CwXML.CodewordAgentCommand command) { CwXML.CodewordAgentResponse response = new CwXML.CodewordAgentResponse(); response.CommandReceiveDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); response.CommandProcessingStartDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); response.ResponseCode = CwConstants.AGENTRESPONSE_OK; response.CommandCodeReceived = command.CommandCode; //------------------------------------------------------------- // // GET SYSTEM INFORMATION // //------------------------------------------------------------- #region GET SYSTEM INFORMATION if (command.CommandCode == CwConstants.AGENTCMD_GETSYSTEMINFO) { response.ResponseInfo = "System information retrieved."; CwXML.CodewordSystemInformation sysinfo = new CwXML.CodewordSystemInformation(); sysinfo.HostInformation = new CwXML.HostInformation(); sysinfo.AgentInformation = new CwXML.AgentInformation(); //host info sysinfo.HostInformation.AgentCurrentDirectory = Environment.CurrentDirectory; sysinfo.HostInformation.MachineName = Environment.MachineName; sysinfo.HostInformation.NumProcessors = Environment.ProcessorCount.ToString(); sysinfo.HostInformation.OSVersionShort = Environment.OSVersion.VersionString; sysinfo.HostInformation.LogicalDrives = string.Join(",", Environment.GetLogicalDrives()); sysinfo.HostInformation.IPAddresses = string.Join(",", AgentScanner.EnvironmentHelper.GetIPAddresses()); sysinfo.HostInformation.OSVersionLong = AgentScanner.EnvironmentHelper.GetOSName(); sysinfo.HostInformation.UserDomainName = Environment.UserDomainName; sysinfo.HostInformation.UserName = Environment.UserName; sysinfo.HostInformation.WorkingSetSize = (Environment.WorkingSet / 1000000).ToString() + "MB"; //agent info sysinfo.AgentInformation.Version = Assembly.GetExecutingAssembly().GetName().ToString(); //use XML settings file in current directory - "CwAgentConfiguration.xml" //this will allow us to deserialize the XML data into class structures CwXML xml = new CwXML(); CwXML.CodewordSettingsTemplate cst = new CwXML.CodewordSettingsTemplate(); try { cst = xml.LoadSettingsXML("CwAgentConfiguration.xml"); } catch (Exception e) { response.ResponseInfo = "There was an error retrieving the agent settings: " + e.Message; } sysinfo.AgentInformation.AgentSettings = cst; //use XML signatures file in current directory - "CwAgentSignatures.xml" //this will allow us to deserialize the XML data into class structures xml = new CwXML(); CwXML.CodewordSignatureTemplate sigs = new CwXML.CodewordSignatureTemplate(); try { sigs = xml.ImportSignatureTemplate("CwAgentSignatures.xml"); } catch (Exception e) { response.ResponseInfo = "There was an error retrieving the agent signatures: " + e.Message; } sysinfo.AgentInformation.AgentSignatures = sigs; //assign sysinfo object to return response response.ResponseSystemInformation = sysinfo; } #endregion //------------------------------------------------------------- // // DOWNLOAD EVIDENCE FILES (COLLECT) // //------------------------------------------------------------- #region DOWNLOAD EVIDENCE FILES (COLLECT) else if (command.CommandCode == CwConstants.AGENTCMD_COLLECT) { //=================================================================== // SEND INTERMEDIATE RESPONSE TO TELL HOST TO START RECEIVING FILES //=================================================================== //send a response then prepare to send WriteConnectionLog("CONNECT: Got command to send evidence files..."); CwXML.CodewordAgentResponse response2 = new CwXML.CodewordAgentResponse(); response2.CommandReceiveDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); response2.CommandProcessingStartDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); response2.ResponseCode = CwConstants.AGENTRESPONSE_OK_RECVFILE; response2.CommandCodeReceived = command.CommandCode; WriteConnectionLog("CONNECT: Sending response..."); //send response try { SendResponse(response2); } catch (Exception ex) { WriteConnectionLog("Failed to send response in preparation for evidence collection: " + ex.Message); response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; response.ResponseInfo = "Failed to send response in preparation for evidence collection: " + ex.Message; return response; } WriteConnectionLog("CONNECT: Sending files.."); //=================================================================== // SEND EVIDENCE FILES //=================================================================== //get list of files tos end CwXML.FileSignatureMatch[] fileSigsToSend = command.CommandCollectOrMitigationTask.SignatureMatches.FileSignatureMatches; int count = 0; //send the files foreach (CwXML.FileSignatureMatch match in fileSigsToSend) { try { SendBinaryFile(match.FullPath); } catch (Exception ex) { response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; response.ResponseInfo = "Failed to send binary file '" + match.FullPath + "': " + ex.Message; break; } count++; } if (response.ResponseCode == CwConstants.AGENTRESPONSE_OK) response.ResponseInfo = "Successfully sent " + count + " evidence files."; } #endregion //------------------------------------------------------------- // // PERFORM MITIGATION TASK // //------------------------------------------------------------- #region PERFORM MITIGATION TASK else if (command.CommandCode == CwConstants.AGENTCMD_MITIGATE) { //the mitigation task is stored in the command object as an anomaly report CwXML.CodewordAgentAnomalyReport MitigationTask = command.CommandCollectOrMitigationTask; if (MitigationTask != null) { CwXML.CodewordAgentSignatureMatches matches = MitigationTask.SignatureMatches; //mitigate registry items if (matches.RegistrySignatureMatches != null) { if (matches.RegistrySignatureMatches.Length > 0) { CwXML.RegistrySignatureMatch[] regMatches = matches.RegistrySignatureMatches; AgentScanner.RegistryHelper RegistryScanner = new AgentScanner.RegistryHelper(); RegistryScanner.LoadNtUserDatFiles(false); RegistryScanner.CleanRegistryFindings(ref regMatches, false); RegistryScanner.LoadNtUserDatFiles(true); response.ResponseLog = RegistryScanner.RegistryHelperLog.ToString(); //assign the matches back to our main object, so the ActionSuccessful variable gets sent back matches.RegistrySignatureMatches = regMatches; } } //mitigate file items if (matches.FileSignatureMatches != null) { if (matches.FileSignatureMatches.Length > 0) { CwXML.FileSignatureMatch[] fileMatches = matches.FileSignatureMatches; AgentScanner.FileHelper FileScanner = new AgentScanner.FileHelper(); FileScanner.CleanFileFindings(ref fileMatches); response.ResponseLog = FileScanner.FileHelperLog.ToString(); //assign the matches back to our main object, so the ActionSuccessful variable gets sent back matches.FileSignatureMatches = fileMatches; } } //mitigate memory items if (matches.MemorySignatureMatches != null) { if (matches.MemorySignatureMatches.Length > 0) { CwXML.MemorySignatureMatch[] memMatches = matches.MemorySignatureMatches; AgentScanner.MemoryHelper MemoryScanner = new AgentScanner.MemoryHelper(); MemoryScanner.CleanMemoryFindings(ref memMatches); response.ResponseLog = MemoryScanner.MemoryHelperLog.ToString(); //assign the matches back to our main object, so the ActionSuccessful variable gets sent back matches.MemorySignatureMatches = memMatches; } } //assign the main object to the response's anomaly report response.ResponseAnomalyReport = new CwXML.CodewordAgentAnomalyReport(); response.ResponseAnomalyReport.SignatureMatches = matches; } else { response.ResponseInfo = "Error completing mitigation task: the mitigation object was null!"; response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; } } #endregion //------------------------------------------------------------- // // START A NEW SCAN // //------------------------------------------------------------- #region START A NEW SCAN else if (command.CommandCode == CwConstants.AGENTCMD_STARTSCAN) { //ENTERPRISE MODE CHECK: //make sure there isnt an already-completed scan from //starting up in enterprise mode. if (EnterpriseModeScanLog != null) { response.ResponseLog = EnterpriseModeScanLog.ToString(); response.ResponseInfo = "These results are from a previous scan issued during agent startup. To run a new scan, please re-issue the scan command."; //clear the enterprise scan results EnterpriseModeScanLog = null; } //otherwise, issue a completely new scan task //warning: this can be a lengthy operation (> 10 min) else { CwXML.CodewordAgentAnomalyReport anomalyReport = new CwXML.CodewordAgentAnomalyReport(); AgentScanner scanner = new AgentScanner(); StringBuilder scannerLog = new StringBuilder(); try { scannerLog = scanner.StartScanTask(ref anomalyReport); } catch (Exception ex) { StreamWriter sw = new StreamWriter("AgentScanLog.txt", false); sw.WriteLine(ex.Message); sw.WriteLine(""); if (AgentScanner.AgentScanLog != null) sw.WriteLine(AgentScanner.AgentScanLog.ToString()); sw.Close(); } if (scannerLog != null && anomalyReport != null) { response.ResponseAnomalyReport = anomalyReport; //invalid xml chars replaced in AgentScanner.StartScanTask() response.ResponseInfo = "Scan complete."; response.ResponseLog = CwXML.ReplaceInvalidXmlChars(scannerLog.ToString()); } else { response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; response.ResponseInfo = "An unrecoverable error occured during the scan."; } } } #endregion //------------------------------------------------------------- // // EXIT, NO MORE COMMANDS // //------------------------------------------------------------- #region EXIT else if (command.CommandCode == CwConstants.AGENTCMD_EXIT || command.CommandCode == CwConstants.AGENTCMD_NOMORECOMMANDS) { // //NO ACTION REQUIRED // } #endregion //------------------------------------------------------------- // // INVALID COMMAND // //------------------------------------------------------------- #region INVALID COMMAND else if (command.CommandCode == CwConstants.AGENTCMD_UNKNOWN) { response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; //the error is stored in this member of the fake command we created earlier response.ResponseInfo = string.Join(",", command.CommandParameters); } #endregion //------------------------------------------------------------- // // RECEIVE NEW SIGNATURE UPDATE FILE // //------------------------------------------------------------- #region RECEIVE NEW SIGNATURE UPDATE FILE else if (command.CommandCode == CwConstants.AGENTCMD_UPDATESIG) { //send a response then prepare to receive WriteConnectionLog("CONNECT: Got command to download new signature file..."); CwXML.CodewordAgentResponse response2 = new CwXML.CodewordAgentResponse(); response2.CommandReceiveDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); response2.CommandProcessingStartDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); response2.ResponseCode = CwConstants.AGENTRESPONSE_OK_SENDFILE; response2.CommandCodeReceived = command.CommandCode; WriteConnectionLog("CONNECT: Sending response..."); //send response try { SendResponse(response2); } catch (Exception ex) { WriteConnectionLog("Failed to send response in preparation for file retrieval: " + ex.Message); response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; response.ResponseInfo = "Failed to send response in preparation for file retrieval: " + ex.Message; return response; } byte[] filedata; WriteConnectionLog("CONNECT: Waiting for file..."); //receive the file try { filedata = ReceiveFile(); } catch (Exception ex) { WriteConnectionLog("Failed to receive file contents: " + ex.Message); response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; response.ResponseInfo = "Failed to receive file contents: " + ex.Message; return response; } WriteConnectionLog("CONNECT: File retrieved, saving locally..."); //overwrite our current XML signature file try { if (File.Exists("CwAgentSignatures.xml")) File.Delete("CwAgentSignatures.xml"); AgentScanner.EnvironmentHelper.BinaryWrite("CwAgentSignatures.XML", filedata); } catch (Exception ex) { WriteConnectionLog("Failed to write new signature file: " + ex.Message); response.ResponseCode = CwConstants.AGENTRESPONSE_FAIL; response.ResponseInfo = "Failed to write new signature file: " + ex.Message; return response; } //success! response.ResponseInfo = "Successfully updated signatures file."; } #endregion response.CommandProcessingEndDate = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss"); return response; }