关于WP插件Slider Revolution Plugin的那个漏洞

2014-09-12 16,143

根据老外的报道《WordPress插件 Slider Revolution Plugin 任意文件下载漏洞来袭》

这个洞在地下黑客论坛已经活跃了很久,不知道老毛子搞黑产是怎么弄的。

我比较不赞同的地方是白帽子点到为止,相比之下,黑帽子因为利益的原因会考虑攻击成本及如何进行批量的自动化和可持续性管理。

本着研究与探索的精神我想思考下如何将攻击利用自动化。即这个议题是:如何批量自动化的将鸡肋漏洞利用最大化

poc很简单,之前一个GET请求就可以了,如此可以想到多种用于Web安全测试的工具可以拿来用,不过本着装13的态度,自己用python脚本解决:

https://github.com/darkr4y/pentest-script/blob/master/py/wp_srp_exp.py

之前研究“大数据”的给了我约2W条的WordPress记录,关于如何正确的收集想需要的数据不在这里的讨论范围。

接着该思考的问题来了,光一个任意文件下载似乎是没有直接的伤害,我能想到比较直接的利用方式就是:

1.利用POC下载wp-config.php
2.测试所处主机的MySQL是否能让用户外连(默认是禁止的,不排除很多虚拟主机套件为了方便会开启或者本来就用的在线远程db)
3.如果可以外连则自动查询管理员用户账户和密码
4.拿到密码之后,可以交由hashcat做简单的字典爆破或者掩码攻击,或者直接提交到哈希的查询接口来查询
5.破除密码之后就简单了,自动登陆利用theme-editor.php或plugin-editor.php来GetShell(部分没权限弹FTP登入提示的可以记录到另外一份Log)中

但是,测试的大部分记录中,很多都是不让外连的主机,考虑到这种情况占大多数,那么如何进一步利用呢。

我们来看看,wp-config.php中除了连接数据库的信息还有哪些是我们想要的,可以发现还有一大堆类似Key的东西。

熟悉WP代码一定可以很快知道,这里略过一部分按图索骥的过程,我们直接代码说话:

WordPress的Cookies验证部分位于 /wp-include/pluggable.php 中的 wp_validate_auth_cookie 函数

11
这里只贴了核心部分

...
$pass_frag = substr($user->user_pass, 8, 4);
$key = wp_hash($username . $pass_frag . '|' . $expiration, $scheme);
$hash = hash_hmac('md5', $username . '|' . $expiration, $key);
if ( ! hash_equals( $hash, $hmac ) ) {
...

然后跟进wp_hash函数看一下

function wp_hash($data, $scheme = 'auth') {
$salt = wp_salt($scheme);
return hash_hmac('md5', $data, $salt);
}

哇靠,这里又有一个 wp_hash 函数,跟进看一下

if ( !function_exists('wp_salt') ) :
/**
* Get salt to add to hashes.
*
* Salts are created using secret keys. Secret keys are located in two places:
* in the database and in the wp-config.php file. The secret key in the database
* is randomly generated and will be appended to the secret keys in wp-config.php.
*
* The secret keys in wp-config.php should be updated to strong, random keys to maximize
* security. Below is an example of how the secret key constants are defined.
* Do not paste this example directly into wp-config.php. Instead, have a
* {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just
* for you.
*
*
* define('AUTH_KEY', ' XakmM%G4Yt>f`z]MON');
* define('SECURE_AUTH_KEY', 'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~');
* define('LOGGED_IN_KEY', '|i|Ux`9 * define('NONCE_KEY', '%:R{[P|,s.KuMltH5}cI;/kz7X>QYR0Z_XnZ@|');
* define('AUTH_SALT', 'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW');
* define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W * define('LOGGED_IN_SALT', '+XSqHc;@Q*K_b|Z?NC[3H!!EONbh.n<+=uKR:>*c(u`g~EJBf#8u#R{mUEZrozmm');
* define('NONCE_SALT', 'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT');
*
*
* Salting passwords helps against tools which has stored hashed values of
* common dictionary strings. The added values makes it harder to crack.
*
* @since 2.5.0
*
* @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php
*
* @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce)
* @return string Salt value
*/
function wp_salt( $scheme = 'auth' ) {
static $cached_salts = array();
if ( isset( $cached_salts[ $scheme ] ) ) {
/**
* Filter the WordPress salt.
*
* @since 2.5.0
*
* @param string $cached_salt Cached salt for the given scheme.
* @param string $scheme Authentication scheme. Values include 'auth',
* 'secure_auth', 'logged_in', and 'nonce'.
*/
return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
}
static $duplicated_keys;
if ( null === $duplicated_keys ) {
$duplicated_keys = array( 'put your unique phrase here' => true );
foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) {
foreach ( array( 'KEY', 'SALT' ) as $second ) {
if ( ! defined( "{$first}_{$second}" ) )
continue;
$value = constant( "{$first}_{$second}" );
$duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] );
}
}
}

$key = $salt = '';
if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) )
$key = SECRET_KEY;
if ( 'auth' == $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) )
$salt = SECRET_SALT;

if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ) ) ) {
foreach ( array( 'key', 'salt' ) as $type ) {
$const = strtoupper( "{$scheme}_{$type}" );x
if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) {
$$type = constant( $const );
} elseif ( ! $$type ) {
$$type = get_site_option( "{$scheme}_{$type}" );
if ( ! $$type ) {
$$type = wp_generate_password( 64, true, true );
update_site_option( "{$scheme}_{$type}", $$type );
}
}
}
} else {
if ( ! $key ) {
$key = get_site_option( 'secret_key' );
if ( ! $key ) {
$key = wp_generate_password( 64, true, true );
update_site_option( 'secret_key', $key );
}
}
$salt = hash_hmac( 'md5', $scheme, $key );
}

$cached_salts[ $scheme ] = $key . $salt;

/** This filter is documented in wp-includes/pluggable.php */
return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
}
endif;

追溯到这里就可以看到$key是如何取得的了,我们再来观察一下登陆前后的Cookies变化 (-_-! 早上一不小心把wp升级了,所以抓到的哈希实际是稍微有改变的,具体是因为wp4.0改了一下验证函数,所以下面的Hash其实是4.0版本的)
登陆后

session	ofg0oUn2JIovSU4xNFDHgd08urnC5jstNYr90c9KvyFLSHIpVV4B72J1s4Bxm3o9
wordpress_149b1e81718ae8239d2e2d3fb2113357	darkray|1410053440|AYaMVIRSO6aBFOHkHIzbSnPEvKUrz1JvS1YzGbI8L8Y|3324fde5766ed949bcab533fc33dc9882f8de2fdc8fc1bbd2d4cafdca9740a06
wordpress_logged_in_149b1e81718ae8239d2e2d3fb2113357	darkray|1410053440|AYaMVIRSO6aBFOHkHIzbSnPEvKUrz1JvS1YzGbI8L8Y|e15b3b8d200b7cd42a26eb90dd37b033b1d0c93833805940d7f0bdd5dfc53878
wordpress_test_cookie	WP+Cookie+check
wp-settings-2	**=c&m8=c&m6=c&m5=c&m1=c&editor=html&m4=c&m0=c&hidetb=1&m2=c&m3=c&m7=c&m10=c&m11=c&imgsize=full&uploader=1&libraryContent=browse&align=none&urlbutton=post&ed_size=903&mfold=o
wp-settings-time-2	1409208009	

登陆前

session	ofg0oUn2JIovSU4xNFDHgd08urnC5jstNYr90c9KvyFLSHIpVV4B72J1s4Bxm3o9
wordpress_test_cookie	WP+Cookie+check
wp-settings-2	**=c&m8=c&m6=c&m5=c&m1=c&editor=html&m4=c&m0=c&hidetb=1&m2=c&m3=c&m7=c&m10=c&m11=c&imgsize=full&uploader=1&libraryContent=browse&align=none&urlbutton=post&ed_size=903&mfold=o
wp-settings-time-2	1409208009

我们来解析一下登陆后的Cookies
wordpress_149b1e81718ae8239d2e2d3fb2113357=darkray|1410053440|AYaMVIRSO6aBFOHkHIzbSnPEvKUrz1JvS1YzGbI8L8Y|3324fde5766ed949bcab533fc33dc9882f8de2fdc8fc1bbd2d4cafdca9740a06
wordpress是固定关键字,下划线后面的是一串MD5,其是由对应站点来生成的既 149b1e81718ae8239d2e2d3fb2113357 = http://www.blackh4t.org
而在cookies的值中
darkray = 当前登陆的用户名(用户名可以很容易的获取)
一串数字 = cookies的过期时间
而最后一长串就是我们的验证Hash了,也是就是加密算法中 $hash = hash_hmac(‘md5′, $username . ‘|’ . $expiration, $key);
走远了,我们知道$key是和wp-config.php中的一些常量相关的,所以这个$key的值在结合本文所述的漏洞中很任意得到,这样依赖我们要关心的就只是 $pass_frag = substr($user->user_pass, 8, 4) 的值了。显然在user_pass的取值是大小写加数字,所以爆破的次数就是
(26 + 26 + 10)^4 = 14776336 约 1.4 kw
对于批量和自动化来说这个数字显得太庞大了,并且受限于管理员登陆的时间所以验证成功的概率不是固定的,如果点了注销也就只能白费功夫了。

但是再定向攻击中,持续的渗透一个目标时不妨可以一试,反正已经自动化,后面的时就是休息等着结果了。
很不幸,WordPress升级4.0后又对Cookies的验证函数做了修改,加入了一个新的$token变量,不过偷懒具体的就没有跟进代码了

$pass_frag = substr($user->user_pass, 8, 4);
$key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
$hash = hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );

最后po一张漏洞的影响范围,2w条随机的WordPress大概成功获取到700左右的wp-config.php,代码中间有很多容错没有处理,比如HTTPS等,但也不会影响数据太多.
2
最后感谢某不愿意透漏id的同学的悉心指导!

参考:
《WordPress cookie伪造漏洞详细分析及exp》 http://www.91ri.org/8759.html
《WordPress 3.8.2 cookie伪造漏洞再分析》 http://drops.wooyun.org/papers/1409
《WordPress 3.8.2补丁分析 HMAC timing attack》 http://drops.wooyun.org/papers/1404

本文作者:DarkRay

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/132.html

Tags:
评论  (12)
快来写下你的想法吧!

DarkRay

文章数:5 积分: 6

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号