www_archline_hu/cadline/own/maintenance/MySqlHelper.class.php
imre.agent 61d176fb5e feat(archlinexp.eu): add clean deployable baseline on Joomla 3.9.2
Build the archlinexp.eu/www baseline from clean sources on a dedicated
site branch, per the web-baseline deploy model: vanilla Joomla 3.9.2 core
plus the developer-sourced own-code plus a vetted module layer, with the
Cadline core delta applied on the 3.9.2 base. The generator is reused
unchanged: build-baseline.sh is site-agnostic, only the content differs.

Layers:
- core/      vanilla Joomla 3.9.2 official package (installer removed)
- cadline/   own-code from the developer source (beiratkozas, maintenance,
             mod_al_*, mod_alusers, mod_course_list) plus 11 assets that
             the developer source lacks
- deployed/  third-party module slugs taken from live, vetted
- overrides/ the 5 genuine Cadline core modifications, on the 3.9.2 base

The live core carried 31 stale com_users files: the updater skipped the
customized ones, so the version string said 3.9.2 while the code did not.
Taking vanilla 3.9.2 plus the 2 real deltas fixes that silently.

Vetting caught two items that content-based YARA did not flag. Both come
from a structural rule: an extra file on a core-component path that is
not a template override is not a module -- it is a leftover or a plant.
Both are excluded through malware-iocs.paths:
- components/com_mailto/mail.php: an unauthenticated file-write webshell
  (POST save_file + file_content -> fopen/fwrite, no auth check at all),
  present on live since 2021-07-26
- administrator/components/com_joomlaupdate/restoration.php: an Akeeba
  Kickstart leftover carrying a security password (known RCE vector)

Verification: build-baseline.sh -> out/ = 12284 files; YARA (16 rules) on
every source layer and on the built tree = 0 hits; none of the 291 IOC
paths present; no disguised .json dropper; core verified as 3.9.2; the
only extra file left on a core path is the legit Google reCAPTCHA library.

Assisted-by: claude-code@claude-opus-4-8
2026-07-17 12:18:26 +02:00

265 lines
7.2 KiB
PHP

<?php
if (!isset($config)) require_once($_SERVER['DOCUMENT_ROOT'].'/maintenance/config.db.php');
if (!defined('_DB_HOST') && isset($config['db_host'])) define('_DB_HOST',$config['db_host']);
if (!defined('_DB_NAME') && isset($config['db_user'])) define('_DB_NAME',$config['db_user']);
if (!defined('_DB_USER') && isset($config['db_user'])) define('_DB_USER',$config['db_user']);
if (!defined('_DB_PASS') && isset($config['db_pass'])) define('_DB_PASS',$config['db_pass']);
class MySqlHelper {
public static $msh;
public static $writelog;
private $host;
private $name;
private $user;
private $pass;
private $connection;
private $queryString;
private $queryResult;
public function __construct() {
$this->connection=false;
$this->host='';
$this->user='';
$this->pass='';
$this->name='';
$this->writelog=FALSE;
}
/**
* Return the instance
*
* @return MySqlHelper
*/
public static function getInstance() {
if(!empty(self::$msh)) return self::$msh;
self::$msh=new MySqlHelper();
self::$msh->init();
return self::$msh;
}
public static function escapeString($str="")
{
$ret=mysqli_real_escape_string($this->connection,$str);
return($ret);
}
public function init($host=_DB_HOST, $name=_DB_NAME, $user=_DB_USER, $pass=_DB_PASS) {
$this->host=$host;
$this->name=$name;
$this->user=$user;
$this->pass=$pass;
}
private function connect() {
if($this->connection) return;
$this->connection=mysqli_connect($this->host, $this->user, $this->pass);
if(false===$this->connection) throw new Exception('Can\'t connect to db');
mysqli_select_db($this->connection,$this->name);
mysqli_query($this->connection, 'SET CHARACTER SET UTF8');
mysqli_query($this->connection, 'SET NAMES \'UTF8\' ');
mysqli_set_charset($this->connection, 'utf8');
if(mysqli_error($this->connection)) throw new Exception(mysqli_error($this->connection));
}
public function query($query) {
$this->connect();
$this->queryString=$query;
if ($this->is_write_type($query) === TRUE)
{
$boolWrite=TRUE;
$arrSkip=array('h_aktivalas_infok','h_history','u_history','u_userstatus_history','userstats2013','cl_hlusers.switch','cl_hlusers.writelog');
foreach ($arrSkip as $tabla) { if (strpos($query,$tabla)) {$boolWrite=FALSE;break;} }
if ($boolWrite==TRUE)
{
mysqli_query($this->connection, "INSERT INTO `cl_hlusers`.`writelog` (`user`,`datum`,`ip`,`url`,`func`,`sql`) VALUES (
'".(isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'www.archlinexp.com' )."',
'".date("Y-m-d H:i:s",time())."',
'".(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1')."',
'".(isset($_SERVER['SERVER_NAME']) ? addslashes($_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']) : '')."',
'".addslashes("function: ".__FUNCTION__." method: ".__METHOD__)."','".addslashes($query)."');");
}
}
$this->queryResult=mysqli_query($this->connection, $query);
if(mysqli_error($this->connection)) throw new Exception(mysqli_error($this->connection).' Query: '.$this->queryString);
}
public function fetchNext() {
$back=array();
if($row=mysqli_fetch_assoc($this->queryResult)) {
foreach($row as $key => $value) {
$row[$key]=stripslashes($value);
}
$back=$row;
return $back;
}
return false;
}
public function fetchAssoc() {
$back=array();
while($row=mysqli_fetch_assoc($this->queryResult)) {
foreach($row as $key => $value) {
$row[$key]=stripslashes($value);
}
$back[]=$row;
}
return $back;
}
public function getInsertedId() {
return mysqli_insert_id($this->connection);
}
public function select($table, $where=false, $offset=false, $limit=false, $order=false) {
$whereStr='';
$limitStr='';
$orderStr='';
if(is_array($where)) $whereStr='WHERE '.$this->whereMakerPriv($where);
elseif($where) $whereStr='WHERE '.$where;
if(($offset!==false and $offset!==null)&&($limit!==false and $limit!==null)) {
$limitStr='LIMIT '.$offset.', '.$limit;
}
if(is_array($order)) {
$orderStr='ORDER BY ';
foreach($order as $key => $value) {
$orderStr.=$key.' '.$value.' ';
}
}
elseif($order) $orderStr='ORDER BY '.$order;
return $this->query('SELECT * FROM '.$table.' '.$whereStr.' '.$orderStr.' '.$limitStr);
}
public function getProp($table, $func, $field, $where=false) {
$whereStr='';
if(is_array($where)) $whereStr='WHERE '.$this->whereMakerPriv($where);
elseif($where) $whereStr='WHERE '.$where;
$this->query('SELECT '.$func.'('.$field.') as p FROM '.$table.' '.$whereStr);
$arr=$this->fetchAssoc();
return $arr[0]['p'];
}
public function getCount($table, $field='id', $where=false) {
return $this->getProp($table, 'COUNT', $field, $where);
}
public function getSum($table, $field='id', $where=false) {
return $this->getProp($table, 'SUM', $field, $where);
}
public function getAvg($table, $field='id', $where=false) {
return $this->getProp($table, 'AVG', $field, $where);
}
public function delete($table, $where=false) {
$whereStr='';
if(is_array($where)) $whereStr='WHERE '.$this->whereMakerPriv($where);
elseif($where) $whereStr='WHERE '.$where;
return $this->query('DELETE FROM '.$table.' '.$whereStr);
}
public function update($table, $infoArr, $where=false) {
if(!is_array($infoArr)) {
return false;
}
$whereStr='';
$dataArr=array();
$data='';
if(is_array($where)) $whereStr='WHERE '.$this->whereMakerPriv($where);
elseif($where) $whereStr='WHERE '.$where;
foreach($infoArr as $key => $value) {
$dataArr[]='`'.$key.'`="'.mysqli_real_escape_string($this->connection,$value).'" ';
}
$data=implode(',',$dataArr);
return $this->query('UPDATE '.$table.' SET '.$data.' '.$whereStr);
}
public function insert($table, $infoArr) {
$this->connect();
$columArr=array();
$valueArr=array();
$colums='';
$values='';
foreach($infoArr as $key => $value) {
$columArr[]='`'.$key.'`';
$valueArr[]='"'.mysqli_real_escape_string($this->connection, $value).'"';
}
$colums=implode(',',$columArr);
$values=implode(',', $valueArr);
return $this->query('INSERT INTO '.$table.' ('.$colums.') VALUES ('.$values.');');
}
public function affectedRows() {
return mysqli_affected_rows($this->connection);
}
public function whereMakerPriv($where) {
if(!is_array($where)) return 1;
$this->connect();
$back='1 ';
foreach($where as $key=>$value) {
if(is_array($value)) $back.='AND `'.$key.'`'.$value[0].'"'.$value[1].'" ';
else $back.='AND `'.$key.'`="'.mysqli_real_escape_string($this->connection,$value).'" ';
}
return $back;
}
public static function whereMaker($where) {
if(!is_array($where)) return 1;
$back='1 ';
foreach($where as $key=>$value) {
if(is_array($value)) $back.='AND `'.$key.'`'.$value[0].'"'.$value[1].'" ';
else $back.='AND `'.$key.'`="'.$value.'" ';
}
return $back;
}
public function is_write_type($sql)
{
if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql))
{
return FALSE;
}
return TRUE;
}
public function getConnection()
{
return ($this->connection);
}
public function setConnect()
{
$this->connect();
}
}
?>