Joomla.it Forum
Non solo Joomla... => Sicurezza => : aletochigno 28 Sep 2011, 17:16:33
-
Scrivo questo post per raccontare come ho potuto risolvere un problema che a quanto pare è molto diffuso,ovvero quando il sito viene hackerato.
Un anno fa inizio a realizzare un sito web appoggiandomi su altervista caricando joomla 1.5.22.
Dopo un po' di tempo speso lavorandoci sopra,per problemi non dipendenti da me lo lascio stare così com'è e smetto di lavorarci.
Qualche settimana fa mi viene chiesto di ricominciare a lavorare sul sito web,e di spostarlo su un hosting a pagamento.Dopo aver acquistato l'hosting vado in ftp su altervista,copio tutti i files e le cartelle ,faccio il backup del database e lo carico su nuovo hosting,ovviamente modificando il file configuration.php con i parametri relativi al nuovo hosting.
Apparentemente con firefox funziona,soltanto che non visualizzo le immagini.
Con firebug noto che il path di ogni immagine corrisponde a quello di altervista...mah.
Va bene,riscrivo i path corretti.
Vado ad aprire il sito con chrome e mi appare la pagina con l'allerta che il sito è pericoloso,che contiene malware e che se decido di entrare è a mio rischio.
Così decido di fare una scansione ,come consigliato, utilizzando gli strumenti per webmasters di google,che ovviamente non rileva assolutamente nulla.
Entro in ftp ,apro il file index.php e vedo che in alto ,le prime due righe contengono dei links che non conosco in mezzo a caratteri alfanumerici random,del tipo a3d0f8jk65f3e22...ecc
Apro il file index.html che stranamente pesa 144kb(troppo per una sola linea di codice) e vedo che anche lì ci sono dei links ,con una parola ricorrente :indometastan.
Inizio a cercare la soluzione sul forum seguendo la strada dell'installazione da zero di joomla e poi la successiva sovrascrittura dei files di backup del sito.
Cosa accade?Che quando vado a digitare l'indirizzo del sito mi appare il testo della pagina index.php come spiegato nel seguente post : http://forum.joomla.it/index.php/topic,145154.msg646965.html#msg646965.
Provo ad installare all'interno di una sottocartella del sito la versione 1.7......idem,visualizza la pagina con il testo scritto nella index.php.........Manca l'interprete php!
Riprovo a cancellare tutto e ricaricare per 4 o 5 volte..estenuante.
Così inizio a cercare degli strumenti più attendibili rispetto a google webmasters tools.
Uno di questi è http://www.ipvoid.com/ che esegue la scansione dell'ip del sito per trovare codici malware.
Un altro è http://sitecheck.sucuri.net/scanner/(la scansione è gratuita,la pulizia automatica del sito no)<posso inserirlo nel forum?>
Questi mi rilevano il malware in varie pagine.
Così metto in sicurezza il sito,mettendolo offline e cambiando tutte le password possibili(ftp,pannello di controllo,DB,accesso admin ecc).
Finalmente riesco a trovare ciò che mi serve:uno script che analizza il sito e mi dice esattamente in quali files ci sono i codici malware.La maggior parte dei files modificati si trovano nei templates,ed inizialmente,prima di usare questo script,li avevo cancellati tutti.Lo trovate qui: http://redleg-redleg.blogspot.com/p/simple-script-to-find-base64decode-in.html
Per utilizzare lo script bisogna incollarlo in un file che chiameremo ad esempio revealer.php e lo copiamo nella root del sito joomla.
Dopodichè aprendo il browser bisogna digitare l'indirizzo del sito ad esempio http://www.miosito.com/revealer.php.
A questo punto lo script inizia a funzionare ed il tempo che può impiegare varia a seconda della grandezza del vostro sito.
Il risultato ottenuto lo potete vedere nella pagina esempio in allegato:
Così non rimane altro che entrare nei files indicati e cancellare le linee scritte dal malware.
Riprovando a scansionare con i tools online descritti sopra finalmente il sito è risultato pulito.
Inoltre navigando con chrome non è più apparso il messaggio di pericolo malware per il sito.
Una nota del creatore dello script di analisi:"Lo script funziona con la maggior parte dei siti web,l'importante è che la versione di php sia dalla 5 in poi."
Spero di esser stato chiaro e soprattutto utile.
[allegato vecchio più di un anno eliminato da un amministratore]
-
Grazie 1000, le info che dai in questo post sono veramente interessanti.
Ciao
J
-
Anche io ho avuto lo stesso problema e ho fatto col file revealer come hai detto tu!
Scusa l'ignoranza ma come faccio a riconoscere quali sono le righe di codice scritte dal malware?
è per caso: $code =' PtxWPFNtVPNtWUS1MKW5VQ0tVyA....????
-
Si,i codici maligni sono all'inizio del file.Alcuni vengono duplicati più in basso.Poi li vedi sono stringhe lunghissime di caratteri alfanumerici..
-
scusa ma tipo in questo file qui non c'è niente giusto???
perchè il revealer.php continua a dirmi che è infetto...
<?php
/**
* Joom!Fish - Multi Lingual extention and translation
manager for Joomla!
* Copyright (C) 2003-2009 Think Network GmbH,
Munich
*
* All rights reserved. The Joom!Fish project is
a set of extentions for
* the content management system Joomla!. It
enables Joomla!
* to manage multi lingual sites especially in all
dynamic information
* which are stored in the
database.
*
* This program is free software; you can
redistribute it and/or
* modify it under the terms of the GNU General
Public License
* as published by the Free Software Foundation; either
version 2
* of the License, or (at your option) any later
version.
*
* This program is distributed in the hope that it
will be useful,
* but WITHOUT ANY WARRANTY; without even the implied
warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the
* GNU General Public License for more
details.
*
* You should have received a copy of the GNU
General Public License
* along with this program; if not, write to the
Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307,USA.
*
* The "GNU General Public License"
(GPL) is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
-----------------------------------------------------------------------------
*
$Id: JCacheStorageJFDB.php 1344 2009-06-18 11:50:09Z akede $
* @package
joomfish
* @subpackage cache
*
*/
// Check to
ensure this file is within the rest of the framework
defined('JPATH_BASE') or
die();
//Register class that don't follow one file per class naming
conventions
JLoader::register('JCacheStorage' , JPATH_LIBRARIES .DS.
'joomla'.DS.'cache' .DS.'storage.php');
/**
* Joomfish Cache
Class
*
* @author Geraint Edwards
(http://www.gwesystems.com)
* @package
Joomfish
* @subpackage Cache
*
@since 2.0
*/
class JCacheStorageJfdb
extends JCacheStorage
{
var $db;
var
$profile_db;
/* Constructor
*
* @access protected
* @param array
$options optional parameters
*/
function
__construct( $options = array() )
{
static $expiredCacheCleaned;
$this->profile_db = & JFactory::getDBO();
$this->db = clone ($this->profile_db);
$this->_language =
(isset($options['language'])) ? $options['language'] : 'en-GB';
$this->_lifetime = (isset($options['lifetime'])) ?
$options['lifetime'] : 60;
$this->_now
= (isset($options['now'])) ? $options['now'] :
time();
$config
=& JFactory::getConfig();
$this->_hash =
$config->getValue('config.secret');
// if
its not the first instance of the joomfish db cache then check if it should be
cleaned and otherwise garbage collect
if
(!isset($expiredCacheCleaned)) {
//
check a file in the 'file' cache to check if we should remove all our db cache
entries since cache manage doesn't handle anything other than file
caches
$conf =&
JFactory::getConfig();
$cachebase =
$conf->getValue('config.cache_path',JPATH_ROOT.DS.'cache');
$cachepath =
$cachebase.DS."joomfish-cache";
if
(!JFolder::exists($cachepath)){
JFolder::create($cachepath);
}
$cachefile =
$cachepath.DS."cachetest.txt";
jimport("joomla.filesystem.file");
if (!JFile::exists($cachefile) || JFile::read($cachefile)!="valid"){
// clean out the whole
cache
$this->cleanCache();
JFile::write($cachefile,"valid");
}
$this->gc();
}
$expiredCacheCleaned =
true;
}
/**
* One
time only DB setup function
*
*/
function setupDB() {
$db = & JFactory::getDBO();
$charset =
($db->hasUTF()) ? 'CHARACTER SET utf8 COLLATE utf8_general_ci' :
'';
$sql = "CREATE TABLE IF NOT EXISTS
`#__dbcache` ("
. "\n `id` varchar ( 32 )
NOT NULL default '',"
. "\n `groupname`
varchar ( 32 ) NOT NULL default '',"
. "\n
`expire` datetime NOT NULL default '0000-00-00 00:00:00',"
//. "\n `value` MEDIUMTEXT NOT NULL default '',"
. "\n `value` MEDIUMBLOB NOT NULL default '',"
. "\n PRIMARY KEY ( `id`,`groupname` ),"
. "\n KEY ( `expire`,`groupname` )"
. "\n
) $charset";
$db->setQuery( $sql );
if (!$db->query()){
echo $db->getErrorMsg()."[br /]";
echo $db->_sql;
}
}
/**
* Get
cached data from a db by id and group
*
* @access public
* @param
string $id The cache
data id
* @param string $group
The cache data group
* @param
boolean $checkTime True to verify cache
time expiration threshold
* @return mixed
Boolean false on failure or a cached data string
* @since 1.5
*/
function get($id, $group, $checkTime)
{
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile();
$hashedid = md5($id.'-'.$this->_hash.'-'.$this->_language);
$data = false;
$sql =
"SELECT UNCOMPRESS(value) FROM `#__dbcache` WHERE
id=".$this->db->quote($hashedid)." AND
groupname=".$this->db->quote($group)." AND
expire>FROM_UNIXTIME(".$this->db->quote($this->_now).")";
$keepSQL =
$this->db->_sql;
$this->db->setQuery($sql);
// Must set
false to ensure that Joomfish doesn't get involved
$data = $this->db->loadResult(false);
$this->db->_sql = $keepSQL
;
if (is_null($data)){
$data = false;
}
else {
//$data
= base64_decode($data);
}
if (method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile($pfunc);
return $data;
}
/**
* Store the data to a file by id and group
*
* @access public
* @param string $id
The cache data id
* @param string
$group The cache data group
*
@param string $data The data to store in
cache
* @return boolean True on
success, false otherwise
* @since
1.5
*/
function store($id, $group,
$data)
{
//$this->db =&
JFactory::getDBO();
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile();
$hashedid = md5($id.'-'.$this->_hash.'-'.$this->_language);
//$data = base64_encode($data);
$sql = "REPLACE INTO `#__dbcache` (id, groupname,expire,value) VALUES
(".$this->db->quote($hashedid).",".$this->db->quote($group).",FROM_UNIXTIME(".$this->db->quote($this->_now
+
$this->_lifetime)."),COMPRESS(".$this->db->quote($data)."))";
$this->db->setQuery($sql);
$res = $this->db->query();
if (method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile($pfunc);
return
$res;
}
/**
*
Remove a cached data file by id and group
*
* @access public
* @param
string $id The cache data
id
* @param string $group
The cache data group
* @return
boolean True on success, false otherwise
* @since 1.5
*/
function remove($id, $group)
{
//$this->db =& JFactory::getDBO();
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile();
$sql =
"DELETE FROM `#__dbcache` WHERE id=".$this->db->quote($id)." AND
groupname=".$this->db->quote($group);
$this->db->_skipSetRefTables=true;
$this->db->setQuery($sql);
$this->db->_skipSetRefTables=false;
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile($pfunc);
return
$this->db->query();
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans
all cache in the group
* notgroup mode :
cleans all cache not in the group
*
* @access public
* @param
string $group The cache data group
* @param string $mode The
mode for cleaning cache [group|notgroup]
* @return
boolean True on success, false otherwise
* @since 1.5
*/
function clean($group, $mode)
{
//$this->db =& JFactory::getDBO();
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile();
$result =
false;
switch (trim($mode))
{
case
'group':
$sql =
"DELETE FROM `#__dbcache` ";
$this->db->setQuery($sql);
break;
default:
$sql =
"DELETE FROM `#__dbcache` WHERE
groupname=".$this->db->quote($group);
$this->db->setQuery($sql);
break;
}
$this->db->_skipSetRefTables=true;
$result
= $this->db->query();
$this->db->_skipSetRefTables=false;
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile($pfunc);
return
$result;
}
/**
*
Clean the whole cache
*
* @return
boolean True on success, false otherwise
*/
function cleanCache()
{
//$this->db =&
JFactory::getDBO();
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile();
$result =
false;
$sql = "DELETE FROM `#__dbcache`
";
$this->db->setQuery($sql);
$this->db->_skipSetRefTables=true;
$result = $this->db->query();
$this->db->_skipSetRefTables=false;
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile($pfunc);
return
$result;
}
/**
*
Garbage collect expired cache data
*
* @access public
* @return boolean True on
success, false otherwise.
*/
function
gc()
{
//$this->db =&
JFactory::getDBO();
if
(method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile();
$sql =
"DELETE FROM `#__dbcache` WHERE expire<
FROM_UNIXTIME(".$this->db->quote($this->_now).")";
$this->db->setQuery($sql);
$this->db->_skipSetRefTables=true;
$result
= $this->db->query();
$this->db->_skipSetRefTables=false;
// if
we can't delete then the database table probably doesn't exist so create
it
if (!$result &&
$this->db->getErrorNum()==1146){
$this->setupDB();
}
if (method_exists($this->profile_db,"_profile")) $pfunc =
$this->profile_db->_profile($pfunc);
}
/**
* Test to see if the cache storage is
available.
*
* @static
* @access public
* @return boolean
True on success, false otherwise.
*/
function test()
{
return
true;
}
}
-
@Faurinz
Cortesemente non credo sia il caso di inserire codice così in abbondanza, almeno inseriscilo nel tag # o ancora meglio visto che è così lungo ti chiedo cortesemente di editare il post ed inserire quel codice in allegato in un file txt in modo da avere il post meglio leggibile, grazie.
-
Che tipo di errore da?(ehm.. se è lungo in allegato va bene)