global $smd_prognostics_checksums, $smd_prognostics_sqlprot;
$smd_prognostics_checksums = rtrim(get_pref('smd_prognostics_dir', txpath, 1), DS) . DS . get_pref('smd_prognostics_prefix', '').'smd_prognostics_checksums.txt';
$hdron = get_pref('smd_prognostics_req_headers', '');
$smd_prognostics_sqlprot = new smd_prog_PhProtector(false);
if (@txpinterface == 'admin') {
global $smd_prognostics_event, $smd_prognostics_style, $txp_user;
$smd_prognostics_event = 'smd_prognostics';
$smd_prognostics_privs = '1,2';
$smd_prognostics_suppress = gps('smd_prognostics_suppress');
$smd_prognostics_style = array(
'msg' =>
'.smd_prog_warn { margin:0 auto 10px; width:500px; background:#ffc; border:2px dashed red; padding:10px; color:#444; }
.smd_prog_warn a { font-weight:bold; color:#963; }
.smd_prog_warn span { font-weight:bold; }
.smd_prog_warn div { font-weight:bold; padding-bottom:10px; }',
'setup' =>
'.smd_label { text-align:right!important; vertical-align:middle; }
.smd_prog_btns form { display:inline; }
.smd_prognostics_setup h3 { margin:25px 0 0; }',
'meter' =>
'#smd_prog_meter { margin:0 auto; border:1px solid #ccc; width:360px; padding:5px; font-size:120%; }
.smd_prog_pass_strength { color:green; font-weight:bold; }',
'advice' =>
'.smd_prog_advice { width:750px; }
#list.smd_prog_advice td { padding:5px; }
.smd_prog_btns { width:140px; }',
);
add_privs($smd_prognostics_event, $smd_prognostics_privs);
add_privs('plugin_prefs.'.$smd_prognostics_event, $smd_prognostics_privs);
register_tab('extensions', $smd_prognostics_event, smd_prognostics_gTxt('smd_prognostics'));
register_callback('smd_prognostics_setup', 'plugin_prefs.'.$smd_prognostics_event);
register_callback('smd_prognostics_dispatcher', $smd_prognostics_event);
register_callback('smd_prognostics_chronostrength', 'admin_side', 'head_end');
if ($hdron) {
register_callback('smd_prognostics_request_headers', 'admin_side', 'head_end');
}
if (strpos(get_pref('smd_prognostics_sql_inject', ''), 'admin') !== false) {
register_callback('smd_prognostics_sql_inject', 'admin_side', 'head_end');
}
$privs = safe_field("privs", "txp_users", "name = '".doSlash($txp_user)."'");
// Admin side callback
if (!$smd_prognostics_suppress && in_array($privs, do_list($smd_prognostics_privs))) {
register_callback('smd_prognostics', 'admin_side', 'pagetop_end');
}
}
// Public side callbacks
if (strpos(get_pref('smd_prognostics_check_where', ''), 'public') !== false) {
register_callback('smd_prognostics', 'pretext');
}
if ($hdron) {
register_callback('smd_prognostics_request_headers', 'pretext');
}
if (strpos(get_pref('smd_prognostics_sql_inject', ''), 'public') !== false) {
register_callback('smd_prognostics_sql_inject', 'pretext');
}
// ------------------------
function smd_prognostics_dispatcher($evt, $stp) {
if(!$stp or !in_array($stp, array(
'smd_prognostics_files',
'smd_prognostics_setup',
'smd_prognostics_advice',
'smd_prognostics_ack',
))) {
smd_prognostics_setup('');
} else $stp();
}
// Stub to call the verification code from the URL
function smd_prognostics($evt, $stp) {
return smd_do_prognostics();
}
// Verify the file checksums
// $mode = 0: normal / 1: no update (return arrays only) / 2: silent update
function smd_do_prognostics($mode=0) {
global $smd_prognostics_event, $smd_prognostics_checksums, $smd_prognostics_style;
$now = time();
$nok = $miss = $added = $allfiles = $hashdown = $hashmod = array();
$tdiff = ($mode==1 || ($now - get_pref('smd_prognostics_lastcheck', 0) > get_pref('smd_prognostics_check_freq', 3600))) ? true : false;
$sumhash = get_pref('smd_prognostics_sumhash', NULL, ($mode==1 ? 1 : 0));
$lookat = get_pref('smd_prognostics_check_for', '');
$adds = (strpos($lookat, 'add') !== false);
$dels = (strpos($lookat, 'delete') !== false);
$mods = (strpos($lookat, 'modify') !== false);
// Parts shamelessly plagiarised from txp_diag
if ($tdiff) {
if ($cs = @file($smd_prognostics_checksums)) {
$hash = md5(smd_prognostics_prep_file($smd_prognostics_checksums));
if ($hash != $sumhash) {
$hashmod[] = $smd_prognostics_checksums;
} else {
foreach ($cs as $c) {
if (preg_match('@^(\S+): \((.*)\)$@', trim($c), $m)) {
list(,$file,$md5) = $m;
if ($dels && !file_exists($file)) {
$miss[] = $file;
} else if ($mods && $md5 != 'NULL') {
if (file_exists($file)) {
$content = smd_prognostics_prep_file($file);
if ((md5($content) != $md5) && ($file != $smd_prognostics_checksums)) {
$nok[] = $file;
}
}
}
$allfiles[] = $file;
}
}
if ($adds) {
$filelist = smd_prognostics_readfiles();
$added = array_diff($filelist, $allfiles);
}
if ($mode != 1) {
set_pref('smd_prognostics_lastcheck', $now, 'smd_prognos', PREF_HIDDEN, 'text_input');
}
}
} else {
// File doesn't exist
$hashdown[] = $smd_prognostics_checksums;
}
}
// Assemble message
if ($nok || $miss || $added || $hashdown || $hashmod) {
if ($mode==1) {
return array($nok, $miss, $added, $hashdown, $hashmod);
} else {
$via = get_pref('smd_prognostics_notify_via', '');
$detect = get_pref('smd_prognostics_lastdetect', '');
$lasts = explode(',', get_pref('smd_prognostics_lastact', 0));
$freqs = explode(',', get_pref('smd_prognostics_alarm_freq', 86400));
if (!isset($lasts[1])) {
$lasts[1] = $lasts[0];
}
if (!isset($freqs[1])) {
$freqs[1] = $freqs[0];
}
$lastact_txp = ($mode < 2 && ($now - $lasts[0] > $freqs[0])) ? true : false;
$lastact_mail = ($mode < 2 && ($now - $lasts[1] > $freqs[1])) ? true : false;
$lastmsg = get_pref('smd_prognostics_lastmsg', '');
$subject = smd_prognostics_gTxt('subject');
$msg = join('|', array_merge($hashdown, $hashmod, $nok, $miss, $added));
if (@txpinterface == 'admin' && strpos($via, 'txp') !== false) {
if ($lastact_txp || ($lastmsg != md5($msg))) {
$out = '
'.$subject.'
'.
(($hashdown) ? '
'.smd_prognostics_gTxt('preamble_hashdown').'
' : '').
(($hashmod) ? '
'.smd_prognostics_gTxt('preamble_hashmod').'
' : '').
(($nok) ? '
'.smd_prognostics_gTxt('preamble_nok').'
' : '').
(($miss) ? '
'.smd_prognostics_gTxt('preamble_miss').'
' : '').
(($added) ? '
'.smd_prognostics_gTxt('preamble_added').'
' : '').
'
'.
smd_prognostics_gTxt('postamble').'
';
set_pref('smd_prognostics_lastact', $now.','.$lasts[1], 'smd_prognos', PREF_HIDDEN, 'text_input');
echo $out;
}
}
if (strpos($via, 'email') !== false) {
if ($lastact_mail || ($lastmsg != md5($msg))) {
$to = get_pref('smd_prognostics_mailto', '');
if ($to) {
$domainparts = do_list(doStrip(serverSet('SERVER_NAME')), '.');
$numparts = count($domainparts);
$domain = $domainparts[$numparts-2] . '.' . $domainparts[$numparts-1];
$reply_to = 'noreply@'.$domain;
$txpdir = hu.trim(dirname($_SERVER['PHP_SELF']), '/\\');
$sep = is_windows() ? "\r\n" : "\n";
$headers = "From: smd_prognostics <$reply_to>".
$sep.'Reply-To: '.$reply_to.
$sep.'X-Mailer: Textpattern'.
$sep.'Content-Transfer-Encoding: 8bit'.
$sep.'Content-Type: text/plain; charset="UTF-8"'.
$sep;
$body =
(($hashdown) ? n.smd_prognostics_gTxt('preamble_hashdown').n.n.join(n,$hashdown) : '').
(($hashmod) ? n.smd_prognostics_gTxt('preamble_hashmod').n.n.join(n,$hashmod) : '').
(($nok) ? n.smd_prognostics_gTxt('preamble_nok').n.n.join(n,$nok) : '').
(($miss) ? n.n.smd_prognostics_gTxt('preamble_miss').n.n.join(n,$miss) : '').
(($added) ? n.n.smd_prognostics_gTxt('preamble_added').n.n.join(n,$added) : '').
n.n.''.smd_prognostics_gTxt('postamble').'';
mail($to, $subject, $body, $headers);
}
set_pref('smd_prognostics_lastact', $lasts[0].','.$now, 'smd_prognos', PREF_HIDDEN, 'text_input');
}
}
set_pref('smd_prognostics_lastmsg', md5($msg), 'smd_prognos', PREF_HIDDEN, 'text_input');
if ($lastmsg != md5($msg)) {
if ($detect == '') {
set_pref('smd_prognostics_lastdetect', $now, 'smd_prognos', PREF_HIDDEN, 'text_input');
} else {
$detect = explode(',',$detect);
set_pref('smd_prognostics_lastdetect', $detect[0].','.$now, 'smd_prognos', PREF_HIDDEN, 'text_input');
}
}
}
}
}
// ---------- Catch unexpected request headers
function smd_prognostics_request_headers($evt, $stp) {
$block = explode('|', get_pref('smd_prognostics_req_headers', ''));
$hdr = serverSet('REQUEST_METHOD');
if (in_array($hdr, $block)) {
echo smd_prognostics_gTxt('nice_try_header');
exit(1);
/*
// Send the forensics off
$to = get_pref('smd_prognostics_mailto_csi', '');
$subject = smd_prognostics_gTxt('subject_csi');
$domainparts = do_list(doStrip(serverSet('SERVER_NAME')), '.');
$numparts = count($domainparts);
$domain = $domainparts[$numparts-2] . '.' . $domainparts[$numparts-1];
$reply_to = 'noreply@'.$domain;
$sep = is_windows() ? "\r\n" : "\n";
$headers = "From: smd_frognostics <$reply_to>".
$sep.'Reply-To: '.$reply_to.
$sep.'X-Mailer: Textpattern'.
$sep.'Content-Transfer-Encoding: 8bit'.
$sep.'Content-Type: text/plain; charset="UTF-8"'.
$sep;
$body = n.smd_prognostics_gTxt('req_not_allowed', array('{req}' => $hdr)).n;
foreach ($_SERVER + $_REQUEST + $_ENV as $key => $var) {
$body .= n.$key.': '.$var;
}
mail($to, $subject, $body, $headers);
*/
}
}
// ---------- SQL injection detection
function smd_prognostics_sql_inject() {
global $smd_prognostics_sqlprot;
if($smd_prognostics_sqlprot->isMalicious()) {
echo smd_prognostics_gTxt('nice_try_inject');
// Send the forensics off
$to = get_pref('smd_prognostics_mailto_csi', '');
if ($to) {
$subject = smd_prognostics_gTxt('subject_csi');
$domainparts = do_list(doStrip(serverSet('SERVER_NAME')), '.');
$numparts = count($domainparts);
$domain = $domainparts[$numparts-2] . '.' . $domainparts[$numparts-1];
$reply_to = 'noreply@'.$domain;
$sep = is_windows() ? "\r\n" : "\n";
$headers = "From: smd_frognostics <$reply_to>".
$sep.'Reply-To: '.$reply_to.
$sep.'X-Mailer: Textpattern'.
$sep.'Content-Transfer-Encoding: 8bit'.
$sep.'Content-Type: text/plain; charset="UTF-8"'.
$sep;
$body = n.smd_prognostics_gTxt('preamble_sql_inject').n;
$body .= 'TXP: ' . txp_version .n. 'PHP: ' . phpversion() .n. 'MySQL: ' . mysql_get_server_info() .n. ((is_callable('apache_get_version')) ? 'Apache: ' . apache_get_version().n : '');
foreach ($_SERVER + $_REQUEST + $_ENV as $key => $var) {
$body .= n.$key.': '.$var;
}
mail($to, $subject, $body, $headers);
}
exit(1);
}
}
// -----------------
// Admin-side panels
// -----------------
// ---------- Alarm acknowledgement
function smd_prognostics_ack($msg='') {
global $smd_prognostics_event, $smd_prognostics_checksums, $prefs;
$submit = gps('submit');
$ignore = gps('ignore');
$csi = gps('csi');
$ack = gps('selected');
$smd_prog_ack = (is_array($ack)) ? $ack : (($ack) ? array($ack) : array());
$out = array();
if ($submit == 'Acknowledge' || $ignore == 'Ignore') {
if ($cs = @file($smd_prognostics_checksums)) {
foreach ($cs as $c) {
if (preg_match('@^(\S+): \((.*)\)$@', trim($c), $m)) {
list(,$file,$md5) = $m;
if (($key = array_search($file, $smd_prog_ack)) !== false) {
if (file_exists($file)) {
$content = smd_prognostics_prep_file($file);
$out[] = $file.': ('.( ($ignore == 'Ignore') ? 'NULL' : md5($content) ).')';
}
// Remove files that already have checksums so we're left with additions
unset($smd_prog_ack[$key]);
} else {
$out[] = trim($c);
}
}
}
// Tack on any new files
foreach ($smd_prog_ack as $file) {
if (file_exists($file) && $file != $smd_prognostics_checksums) {
$content = smd_prognostics_prep_file($file);
$out[] = $file.': ('.( ($ignore == 'Ignore') ? 'NULL' : md5($content) ).')';
}
}
$fh = fopen($smd_prognostics_checksums, "w");
fwrite($fh, join(n, $out));
fclose($fh);
smd_prognostics_self_hash();
$msg = smd_prognostics_gTxt('acked');
}
}
if ($csi && $smd_prog_ack) {
$msg = smd_prognostics_gTxt('csi_sent');
} else if ($csi) {
$msg = smd_prognostics_gTxt('none_selected');
}
pagetop(smd_prognostics_gTxt('smd_prognostics'), $msg);
extract(smd_prognostics_buttons());
list($nok, $miss, $added, $hashdown, $hashmod) = smd_do_prognostics(1);
$nok = is_array($nok) ? $nok : array();
$miss = is_array($miss) ? $miss : array();
$added = is_array($added) ? $added : array();
$hashmod = is_array($hashmod) ? $hashmod : array();
$errnum = count(array_merge($nok, $miss, $added, $hashmod));
$forensics = array();
$lform = 'Y-m-d H:i:s';
echo $btnCSS.startTable('list');
echo tr(tda(strong(smd_prognostics_gTxt('ttl_ack')), ' colspan="3"') . tda($btnSetup.$btnFiles.$btnAdvice, $btnStyle) );
echo '';
echo endTable();
if ($csi && $forensics) {
// Send the forensics off
$to = get_pref('smd_prognostics_mailto_csi', '');
if ($to) {
$subject = smd_prognostics_gTxt('subject_csi');
$domainparts = do_list(doStrip(serverSet('SERVER_NAME')), '.');
$numparts = count($domainparts);
$domain = $domainparts[$numparts-2] . '.' . $domainparts[$numparts-1];
$reply_to = 'noreply@'.$domain;
$sep = is_windows() ? "\r\n" : "\n";
$headers = "From: smd_frognostics <$reply_to>".
$sep.'Reply-To: '.$reply_to.
$sep.'X-Mailer: Textpattern'.
$sep.'Content-Transfer-Encoding: 8bit'.
$sep.'Content-Type: text/plain; charset="UTF-8"'.
$sep;
$body =
n.'TXP: ' . txp_version .n. 'PHP: ' . phpversion() .n. 'MySQL: ' . mysql_get_server_info() .n. ((is_callable('apache_get_version')) ? 'Apache: ' . apache_get_version().n : '').
((isset($forensics['nok'])) ? n.smd_prognostics_gTxt('preamble_nok').n.join(n,$forensics['nok']) : '').
((isset($forensics['miss'])) ? n.n.smd_prognostics_gTxt('preamble_miss').n.join(n,$forensics['miss']) : '').
((isset($forensics['added'])) ? n.n.smd_prognostics_gTxt('preamble_added').n.join(n,$forensics['added']) : '').
((isset($forensics['txp_log'])) ? n.n.smd_prognostics_gTxt('preamble_txplog').n.join(n,$forensics['txp_log']) : n.n.smd_prognostics_gTxt('no_log_entries'));
if (isset($forensics['files'])) {
$body .= n.n.smd_prognostics_gTxt('preamble_files');
foreach ($forensics['files'] as $fn => $content) {
$body .= n.n.$fn.n.n.$content.n;
}
}
mail($to, $subject, $body, $headers);
}
}
}
// ---------- File management
function smd_prognostics_files($msg='') {
global $smd_prognostics_event, $smd_prognostics_checksums;
extract(doSlash(gpsa(array('submit'))));
$smd_prognostics_files = gps('smd_prognostics_files');
if (!is_array($smd_prognostics_files)) {
$smd_prognostics_files = array();
}
$adds = (strpos(get_pref('smd_prognostics_check_for'), 'add') !== false);
$filelist = smd_prognostics_readfiles();
$allcount = count($filelist);
if ($submit == 'Save') {
$outfile = array();
foreach ($smd_prognostics_files as $file) {
$content = smd_prognostics_prep_file($file);
$outfile[] = $file.': ('.md5($content).')';
}
$fh = fopen($smd_prognostics_checksums, "w");
fwrite($fh, join(n, $outfile));
if ($adds) {
$additions = array_diff($filelist, $smd_prognostics_files);
$added = array();
foreach ($additions as $addition) {
$added[] = $addition.': (NULL)';
}
fwrite($fh, n.join(n, $added));
}
fclose($fh);
smd_prognostics_self_hash();
smd_do_prognostics(2); // Silently acknowledge all the files
set_pref('smd_prognostics_lastdetect', '', 'smd_prognos', PREF_HIDDEN, 'text_input');
$msg = smd_prognostics_gTxt('files_updated');
}
pagetop(smd_prognostics_gTxt('smd_prognostics'), $msg);
extract(smd_prognostics_buttons());
$smd_prognostics_files = array();
if ($cs = @file($smd_prognostics_checksums)) {
foreach ($cs as $c) {
if (preg_match('@^(\S+): \((.*)\)$@', trim($c), $m)) {
list(,$file,$md5) = $m;
if ($md5 != 'NULL') {
$smd_prognostics_files[] = $file;
}
}
}
}
$moncount = count($smd_prognostics_files);
if ($filelist) {
$filez = array();
foreach($filelist as $key => $val) {
$filez[$val] = $filelist[$key];
}
$filesel = smd_prognostics_multisel('smd_prognostics_files', $filez, $smd_prognostics_files);
}
$showfiles = (isset($filesel) && $filesel);
echo $btnCSS.startTable('list');
echo tr(tda(strong(smd_prognostics_gTxt('ttl_files'))) . tda($btnSetup.$btnAck.$btnAdvice, $btnStyle) );
echo tr(tdcs(smd_prognostics_gTxt('currmon', array('{curr}' => $moncount, '{outof}' => $allcount)) .(($showfiles) ? br.br. smd_prognostics_gTxt('monfiles_explain') : ''), 1, 400));
echo '';
echo endTable();
}
// ---------- Setup / prefs
function smd_prognostics_setup($msg='') {
global $smd_prognostics_event, $smd_prognostics_checksums, $prefs;
pagetop(smd_prognostics_gTxt('smd_prognostics'), $msg);
$origloc = get_pref('smd_prognostics_listloc', realpath(txpath.DS.'..'.DS).DS, 1);
$origdir = rtrim(get_pref('smd_prognostics_dir', realpath(txpath), 1), DS);
$origpfx = get_pref('smd_prognostics_prefix', '', 1);
$preflist = array(
'smd_prognostics_check_freq',
'smd_prognostics_alarm_freq',
'smd_prognostics_check_where',
'smd_prognostics_mailto',
'smd_prognostics_mailto_csi',
'smd_prognostics_dir',
'smd_prognostics_prefix',
'smd_prognostics_listloc',
'smd_prognostics_excludir',
'smd_prognostics_ignores',
);
$warnloc = '';
$notify = gps('smd_prognostics_notify_via');
$chfor = gps('smd_prognostics_check_for');
$reqs = gps('smd_prognostics_req_headers');
$sqlin = gps('smd_prognostics_sql_inject');
$smd_prognostics_notify_via = join('|', ((is_array($notify)) ? $notify : array($notify)));
$smd_prognostics_check_for = join('|', ((is_array($chfor)) ? $chfor : array($chfor)));
$smd_prognostics_req_headers = join('|', ((is_array($reqs)) ? $reqs : array($reqs)));
$smd_prognostics_sql_inject = join('|', ((is_array($sqlin)) ? $sqlin : array($sqlin)));
// Grab the saved pref values and tack on the array item(s)
extract(doSlash(gpsa(array_merge(array('submit'), $preflist))));
$preflist[] = 'smd_prognostics_notify_via';
$preflist[] = 'smd_prognostics_check_for';
$preflist[] = 'smd_prognostics_req_headers';
$preflist[] = 'smd_prognostics_sql_inject';
$smd_prognostics_dir = rtrim($smd_prognostics_dir, DS);
if ($submit == 'Save') {
if (($smd_prognostics_dir != $origdir) || ($smd_prognostics_prefix != $origpfx)) {
if (is_dir($smd_prognostics_dir) && is_writable($smd_prognostics_dir)) {
// Everything OK so do nothing for now
} else {
// Ignore new dir and reset it to what it was before
$smd_prognostics_dir = $origdir;
}
// Room for more config files as and when they are required
$filelist[] = $smd_prognostics_checksums;
foreach ($filelist as $file) {
$filename = $smd_prognostics_prefix.ltrim(basename($file), $origpfx);
rename($file, rtrim($smd_prognostics_dir, DS).DS.$filename);
}
}
// Write all the prefs
foreach ($preflist as $prefval) {
set_pref($prefval, $$prefval, 'smd_prognos', PREF_HIDDEN, 'text_input');
}
if ($smd_prognostics_listloc != $origloc) {
$warnloc = smd_prognostics_gTxt('warn_loc');
}
}
$smd_prognostics_dir = get_pref('smd_prognostics_dir', txpath, 1);
$smd_prognostics_prefix = get_pref('smd_prognostics_prefix', '', 1);
$smd_prognostics_check_for = get_pref('smd_prognostics_check_for', 'delete|modify', 1);
$smd_prognostics_check_where = get_pref('smd_prognostics_check_where', 'admin', 1);
$smd_prognostics_check_freq = get_pref('smd_prognostics_check_freq', 3600, 1);
$smd_prognostics_alarm_freq = get_pref('smd_prognostics_alarm_freq', 86400, 1);
$smd_prognostics_notify_via = get_pref('smd_prognostics_notify_via', '', 1);
$smd_prognostics_mailto = get_pref('smd_prognostics_mailto', '', 1);
$smd_prognostics_mailto_csi = get_pref('smd_prognostics_mailto_csi', '', 1);
$smd_prognostics_listloc = get_pref('smd_prognostics_listloc', realpath(txpath.DS.'..'.DS).DS, 1);
$smd_prognostics_excludir = get_pref('smd_prognostics_excludir', 'images, files, tmp', 1);
$smd_prognostics_ignores = get_pref('smd_prognostics_ignores', 'error_log', 1);
$smd_prognostics_req_headers = get_pref('smd_prognostics_req_headers', 'TRACE|PUT|DELETE', 1);
$smd_prognostics_sql_inject = get_pref('smd_prognostics_sql_inject', '', 1);
extract(smd_prognostics_buttons());
$adds = (strpos($smd_prognostics_check_for, 'add') !== false);
$dels = (strpos($smd_prognostics_check_for, 'delete') !== false);
$mods = (strpos($smd_prognostics_check_for, 'modify') !== false);
$rh_trace = (strpos($smd_prognostics_req_headers, 'TRACE') !== false);
$rh_put = (strpos($smd_prognostics_req_headers, 'PUT') !== false);
$rh_del = (strpos($smd_prognostics_req_headers, 'DELETE') !== false);
$sqlpub = (strpos($smd_prognostics_sql_inject, 'public') !== false);
$sqladm = (strpos($smd_prognostics_sql_inject, 'admin') !== false);
$helpLink = "?event=plugin&step=plugin_help&name=$smd_prognostics_event#smd_setup";
echo $btnCSS.startTable('list', '', 'smd_prognostics_setup');
echo tr(tdcs(strong(smd_prognostics_gTxt('ttl_setup')).sp.smd_prognostics_gTxt('help_link', array('{link}' => $helpLink)), 2) . tda($btnFiles.$btnAck.$btnAdvice, $btnStyle) );
echo '';
echo endTable();
}
// ---------- Security advice
function smd_prognostics_advice($msg='') {
global $smd_prognostics_event, $smd_prognostics_style, $prefs;
pagetop(smd_prognostics_gTxt('smd_prognostics'), $msg);
extract(smd_prognostics_buttons());
$checks = array();
// Prognostics dir in docroot?
if (strpos(get_pref('smd_prognostics_dir', txpath), $prefs['path_to_site']) !== false) {
$checks[] = smd_prognostics_gTxt('docroot_prognostics');
}
// Setup still exists?
if (@is_dir(txpath . DS. 'setup')) {
$checks[] = smd_prognostics_gTxt('setup_exists');
}
// Files dir in docroot?
if (strpos($prefs['file_base_path'], $prefs['path_to_site']) !== false) {
$checks[] = smd_prognostics_gTxt('docroot_files');
}
// Tmp dir in docroot?
if (strpos($prefs['tempdir'], $prefs['path_to_site']) !== false) {
$checks[] = smd_prognostics_gTxt('docroot_tmp');
}
/*
// TODO: check what SHOW GRANTS returns and determine if it could be a security risk
$res = safe_query('SHOW GRANTS');
if (numRows($res) > 0) {
$checks[] = smd_prognostics_gTxt('show_grants');
}
*/
// Does this MySQL user have FILES privs?
$randfile = $prefs['tempdir'].DS.rand().time().'.sql';
$res = @safe_query('SELECT id INTO OUTFILE "'.$randfile.'" FIELDS TERMINATED BY "\t" LINES TERMINATED BY "\n" FROM txp_image WHERE 1 LIMIT 1');
if (mysql_error() == '') {
$checks[] = smd_prognostics_gTxt('sql_file_privs');
if (is_file($randfile)) {
@unlink($randfile);
}
}
echo ''.startTable('list', '', 'smd_prog_advice');
echo tr(tda(strong(smd_prognostics_gTxt('ttl_advice'))) . tda($btnSetup.$btnFiles.$btnAck, $btnStyle) );
if ($checks) {
foreach($checks as $check) {
echo tr(tda($check));
}
} else {
smd_prognostics_gTxt('tight');
}
echo endTable();
}
// ---------- Hash the hashfile
function smd_prognostics_self_hash() {
global $smd_prognostics_checksums;
if (file_exists($smd_prognostics_checksums)) {
$content = smd_prognostics_prep_file($smd_prognostics_checksums);
$hash = md5($content);
} else {
$hash = NULL;
}
set_pref('smd_prognostics_sumhash', $hash, 'smd_prognos', PREF_HIDDEN, 'text_input');
}
// ---------- Compile list of files to monitor
function smd_prognostics_readfiles() {
$filelist = array();
$smd_prognostics_listloc = get_pref('smd_prognostics_listloc', '');
$smd_prognostics_excludir = do_list(get_pref('smd_prognostics_excludir', ''));
$smd_prognostics_ignores = get_pref('smd_prognostics_ignores', '');
if ($smd_prognostics_listloc) {
foreach (do_list($smd_prognostics_listloc) as $loc) {
// NOTE: not using GLOB_BRACE to grab regular and dot files, since it's not always available cross-OS
$filelist = array_merge($filelist, smd_prognostics_rglob("*", GLOB_MARK, $loc, $smd_prognostics_excludir, $smd_prognostics_ignores), smd_prognostics_rglob(".*", GLOB_MARK, $loc, $smd_prognostics_excludir, $smd_prognostics_ignores));
}
}
return $filelist;
}
// ---------- Prepare file contents for md5. Assumes file exists
function smd_prognostics_prep_file($file) {
$content = file_get_contents($file);
$content = str_replace('$'.'HeadURL: http:', '$'.'HeadURL: https:', str_replace("\r\n", "\n", $content));
return $content;
}
// Frankensteined from http://snipplr.com/view/16233/recursive-glob/
function smd_prognostics_rglob($pattern, $flags=0, $path='', $excl=array(), $ign='') {
global $smd_prognostics_checksums;
// Add any files to permanently exclude here (NOT dirs: they're ignored automatically)
$ignarr = ($ign) ? do_list($ign) : array();
$permexclude = array_merge(array(basename($smd_prognostics_checksums)), $ignarr);
if (!$path && ($dir = dirname($pattern)) != '.') {
if ($dir == '\\' || $dir == DS) $dir = '';
return smd_prognostics_rglob(basename($pattern), $flags, $dir . DS, $excl, $ign);
}
$paths = glob($path . '*', GLOB_ONLYDIR | GLOB_NOSORT);
$files = glob($path . $pattern, $flags);
foreach ($paths as $p) {
$pinfo = array_pop(explode(DS, $p));
if (!in_array($pinfo, $excl)) {
$files = array_merge($files, smd_prognostics_rglob($pattern, $flags, $p . DS, $excl, $ign));
foreach($files as $idx => $theFile) {
$fex = array_pop(explode(DS, $theFile));
if($fex=='' || in_array($fex, $permexclude)) {
unset($files[$idx]);
}
}
}
}
return $files;
}
// Format forensic data for a file
function smd_prognostics_forensic_output($file, $fi) {
global $prefs;
$dform = $prefs['dateformat'];
$sep = n;
$out = '';
$out .= $sep . smd_prognostics_gTxt('stat_name') . $file;
$out .= $sep . smd_prognostics_gTxt('stat_size') . $fi['size'];
$out .= $sep . smd_prognostics_gTxt('stat_mod') . strftime($dform, $fi['mtime']);
$out .= $sep . smd_prognostics_gTxt('stat_uid') . $fi['uid'];
$out .= $sep . smd_prognostics_gTxt('stat_gid') . $fi['gid'];
return $out;
}
// Common buttons
function smd_prognostics_buttons() {
global $smd_prognostics_event, $smd_prognostics_style;
$ret = array (
'btnSave' => fInput('submit', 'submit', gTxt('save'), 'publish'),
'btnAck' => '',
'btnAckIt' => fInput('submit', 'submit', smd_prognostics_gTxt('pnl_ackit'), 'publish'),
'btnIgnore' => fInput('submit', 'ignore', smd_prognostics_gTxt('pnl_ignore'), 'publish'),
'btnCSI' => fInput('submit', 'csi', smd_prognostics_gTxt('pnl_csi'), 'publish'),
'btnAdvice' => '',
'btnFiles' => '',
'btnSetup' => '',
'btnStyle' => ' class="smd_prog_btns" style="border:0;height:25px;"',
'btnCSS' => '',
);
return $ret;
}
// Multi-file dropdown selection
function smd_prognostics_multisel($selname='', $tree=array(), $sel=array()) {
$out[] = '';
return join('',$out);
}
// Password strength meter
function smd_prognostics_chronostrength($evt, $stp) {
global $event, $smd_prognostics_style;
if ($event != 'admin') {
return false;
}
echo <<{$smd_prognostics_style['meter']}
EOS;
}
//****************************************************************
// Web page : http://code.google.com/p/phprotector
// Autor : Hugo Sousa adamastor666@gmail.com
// Date : 2010-03-25
// Version : 0.3.1.1
//
//***************************************************************
class smd_prog_PhProtector {
var $SHOW_ERRORS;
public function smd_prog_PhProtector($show_errors) {
$this->SHOW_ERRORS=$show_errors;
if ($this->SHOW_ERRORS) {
error_reporting(E_ERROR | E_WARNING | E_PARSE); //Show errors
ini_set('display_errors', "1"); //display errors
} else {
ini_set('display_errors', "0"); //display errors
ini_set('log_errors', "1"); //log_errors
}
}
/*
* Main function to be called in a index page that redirects to other pages
*
*/
public function isMalicious() {
$sqli = false;
$num_bad_words1 = $this->CheckGet();
$num_bad_words2 = $this->CheckPost();
if ($num_bad_words1 > 0) {
$sqli = true;
}
if ($num_bad_words2 > 0) {
$sqli = true;
}
return $sqli;
}
//check for sql injection and XSS in Post variables
private function CheckPost() {
$num_bad_words = 0;
foreach($_POST as $campo => $input) {
$_POST[$campo]= doArray($_POST[$campo], 'htmlentities'); // XSS PROTECTION
$num_bad_words = $num_bad_words + $this->wordExists($input); //SQL INJECTION
}
return $num_bad_words;
}
//check for sql injection and XSS in GET variables
private function CheckGet() {
$num_bad_words = 0;
foreach($_GET as $campo => $input){
$_GET[$campo]= doArray($_GET[$campo], 'htmlentities'); // XSS PROTECTION
if($this->isIdInjection($campo,$input)){ //SQL ID INJECTION
$num_bad_words = $num_bad_words + 0.5;
}
$num_bad_words = $num_bad_words + $this->wordExists($input); //SQL INJECTION
}
return $num_bad_words;
}
/**
* return true if injection sql word is found.
* The input is tested if is equal to a sql injection pattern
* \b[^a-z]*?drop[^a-z]*?\b
* http://www.pagecolumn.com/tool/regtest.htm
**//* "/*","+" */
private function wordExists($input) {
$num_bad_words = 0;
/*
WORD AFTER
*/
$baddelim1 = "[^a-z]*"; //the delim should be from "a" to "b" anything else is considered sql injection :)
$baddelim2 = "[^a-z]+";
$badwords= array("union", "select", "show", "insert", "update", "delete", "drop", "truncate", "create", "load_file", "exec", "#", "--");
//"/*"
foreach($badwords as $badword) {
$expression = "/".$baddelim1.strtolower($badword).$baddelim2."/";
if (preg_match ($expression, strtolower($input))) {
//die("sql injection!");
$num_bad_words++;
}
}
/*
BEFORE WORD
*/
$baddelim1 = "[^a-z]+"; //the delim should be from "a" to "b" anything else is considered sql injection :)
$baddelim2 = "[^a-z]*";
$badwords= array("@@version", "@@datadir", "user", "version");
foreach($badwords as $badword) {
$expression = "/".$baddelim1.strtolower($badword).$baddelim2."/";
if (preg_match ($expression, strtolower($input))) {
//die("sql injection!");
$num_bad_words++;
}
}
/*
BEFORE WORD AFTER
*/
$baddelim1 = "[^a-z]+"; //the delim should be from "a" to "b" anything else is considered sql injection :)
$baddelim2 = "[^a-z]+";
$badwords= array("benchmark", "--", "varchar", "convert", "char", "limit", "information_schema","table_name", "from", "where", "order");
foreach($badwords as $badword) {
$expression = "/".$baddelim1.strtolower($badword).$baddelim2."/";
if (preg_match ($expression, strtolower($input))) {
//die("sql injection!");
$num_bad_words++;
}
}
return $num_bad_words;
}
/**
* return true if and ID is not really an ID!
*
**/
private function isIDInjection($campo,$input) {
$reg="/^id/";
if(preg_match($reg, $campo)) {
if(!$this->stringIsNumberNotZero($input) || $input == '') {
return true; // if is ID and NOT INTEGER or NULL -> SQL INJECTION!!
}
}
return false;
}
/**
* return true if the string is a number (diferent from 0, the id could not be zero!)
*
**/
private function stringIsNumberNotZero( $string ) {
$i=0;
while ( $i < strlen($string) ) {
if ($string{$i} == "0" && $i==0)
return false;
//verifica se é numero
if ( $string{$i} != "0" &&
$string{$i} != "1" &&
$string{$i} != "2" &&
$string{$i} != "3" &&
$string{$i} != "4" &&
$string{$i} != "5" &&
$string{$i} != "6" &&
$string{$i} != "7" &&
$string{$i} != "8" &&
$string{$i} != "9")
return false;
$i++;
}//while
return true;
}
} //end class
// ------------------------
// Plugin-specific replacement strings - localise as required
// TODO: Textpack this
function smd_prognostics_gTxt($what, $atts = array()) {
global $prefs, $smd_prognostics_event;
$lang = array(
'alarm_freq' => 'Alarm on detection and every: ',
'acked' => 'Alarms acknowledged.',
'ch_add' => 'Additions',
'ch_del' => 'Deletions',
'ch_mod' => 'Modifications',
'check_for' => 'Check files for: ',
'check_freq' => 'Check files (at most) every: ',
'check_where' => 'Check files on public side clicks: ',
'csi' => 'Gather forensics and send for analysis',
'csi_sent' => ' Forensics data sent',
'csv' => '(comma-separated)',
'currmon' => 'You are currently monitoring {curr} out of {outof} available files.',
'docroot_files' => 'Consider moving the "files" folder outside of your site root folder. You can do this via the "Admin > Prefs > Advanced > File directory path" setting.',
'docroot_prognostics' => 'Please change the "Prognostics folder" setting to a directory outside your site root folder.',
'docroot_tmp' => 'Consider moving the "tmp" folder outside of your site root folder. You can do this via the "Admin > Prefs > Advanced > Temporary directory path" setting.',
'excludir' => 'Exclude folders: ',
'files_updated' => 'File list updated',
'help_link' => '(Help)',
'ignores' => 'Ignore files: ',
'lbl_changed' => 'Changed',
'lbl_hashmod' => 'COMPROMISED',
'lbl_missing' => 'Missing',
'lbl_added' => 'Added (not monitored)',
'listloc' => 'File locations: ',
'mailto' => 'Send e-mail to: ',
'mailto_csi' => 'Send forensics to: ',
'monfiles_explain' => 'Select all the files you wish to monitor and click Save. Altering this list will automatically acknowledge any outstanding alarms against the selected files.',
'nice_try_header' => 'Nice try, but that HTTP request is blocked.',
'nice_try_inject' => 'Your request has been denied by smd_prognostics. Nice try.',
'no_alarms' => 'No alarms to acknowledge at present.',
'no_files' => 'No files to list. Check the plugin setup (and Save it).',
'no_log_entries' => 'No log activity around the time of the differences.',
'none_selected' => 'No files selected',
'notify_email' => 'E-mail',
'notify_txp' => 'TXP interface',
'notify_via' => 'Notify via: ',
'preamble_added' => 'These files have been added: ',
'preamble_files' => 'File contents follows: ',
'preamble_hashdown' => 'WARNING! The checksums file is missing.',
'preamble_hashmod' => 'WARNING! The checksums file has been altered: ',
'preamble_miss' => 'These files are missing: ',
'preamble_nok' => 'These files differ from their expected content: ',
'preamble_sql_inject' => 'Possible SQL injection detected',
'preamble_txplog' => 'Log entries prior to this detection: ',
'progloc' => 'Prognostics folder: ',
'progpfx' => 'Unique prefix: ',
'pnl_ack' => 'Alarms',
'pnl_ackit' => 'Acknowledge',
'pnl_advice' => 'Advice',
'pnl_csi' => 'Send Forensics',
'pnl_files' => 'Files',
'pnl_ignore' => 'Ignore',
'pnl_setup' => 'Setup',
'postamble' => 'Acknowledge alarms',
'req_headers' => 'Block request headers: ',
'req_not_allowed' => 'REQUEST_METHOD not allowed: {req}',
'rh_del' => 'DELETE',
'rh_put' => 'PUT',
'rh_trace' => 'TRACE',
'ttl_ack' => 'Prognostic alarm acknowledgement',
'ttl_advice' => 'Prognostic advice',
'ttl_files' => 'Prognostic file monitoring',
'ttl_fileopts' => 'Filesystem options',
'ttl_monopts' => 'Monitoring options',
'ttl_realopts' => 'Realtime options',
'ttl_setup' => 'Prognostic setup',
'smd_prognostics' => 'Prognostics',
'seconds' => '(seconds)',
'setup_exists' => 'The setup directory still exists. Please delete it.',
'show_grants' => 'Your TXP user can access the MySQL privileges in the DB. Consider accessing the DB using a less privileged user.',
'sql_file_privs' => 'Your MySQL user has FILE privileges. Consider revoking this right unless absolutely necessary.',
'sql_inadm' => 'Admin side',
'sql_inject' => '(experimental) Check for SQL injection attacks on: ',
'sql_inpub' => 'Public side',
'stat_gid' => 'Group ID: ',
'stat_mod' => 'Modified: ',
'stat_name' => 'Filename: ',
'stat_size' => 'Size: ',
'stat_uid' => 'User ID: ',
'subject' => 'Prognostics ('.$prefs['siteurl'].')',
'subject_csi' => 'Prognostics ('.$prefs['siteurl'].') forensics',
'tight' => 'You run a pretty tight ship. Stay frosty.',
'warn_loc' => 'File locations have changed. Ensure you update your list of files now',
);
return strtr($lang[$what], $atts);
}