wordpress的4.2.3思路分析和4.8.2sql分析

正文:

现在的wordpress是上线自动更新,且目前wordpress一般个人是不会开启用户注册功能的,不过对于论坛等多用户的平台具有启发(这个思路真的很棒
这两个都是比较老的版本了,具体的没有去专门复现,查看了下源码和有些地方的数据
这两个的利用条件:开启用户注册,且用户有编辑权限
产生的大体原因:拥有一般用户账号的增加,删除,评论等功能数据操作检查不全;任何跳过检查和判断的途径都可能有风险
提示:具有操作数据库的地方,就应该检查操作的数据

一.wordpress4.2.2分析

参考https://security.tencent.com/index.php/blog/msg/93
跟着p师傅的思路走一遍,一层一层的观察函数是否有漏洞,都是对我们传入数据的判断和检查
1.GP混用(get和post参数没有全部进行判断检查)
Wordpress检查用户权限是调用current_user_can函数,我们看到这个函数:

function current_user_can( $capability ) {
	$current_user = wp_get_current_user();
	if ( empty( $current_user ) )
		return false;
	$args = array_slice( func_get_args(), 1 );
	$args = array_merge( array( $capability ), $args );
	return call_user_func_array( array( $current_user, 'has_cap' ), $args );
}

has_cap函数(有几个这个函数,在不同类中)

	public function has_cap( $cap ) {
		if ( is_numeric( $cap ) ) {
			_deprecated_argument( __FUNCTION__, '2.0', __('Usage of user levels by plugins and themes is deprecated. Use roles and capabilities instead.') );
			$cap = $this->translate_level_to_cap( $cap );
		}
		$args = array_slice( func_get_args(), 1 );
		$args = array_merge( array( $cap, $this->ID ), $args );
		$caps = call_user_func_array( 'map_meta_cap', $args );
		// Multisite super admin has all caps by definition, Unless specifically denied.
		if ( is_multisite() && is_super_admin( $this->ID ) ) {
			if ( in_array('do_not_allow', $caps) )
				return false;
			return true;
		}

map_meta_cap函数(这是switch语句中的,switch的break,如果post的数据不存在,就直接结束,args[0]是文章id)(提了个醒,任何跳过检查和判断的途径都可能有风险)

	case 'edit_post':
	case 'edit_page':
		$post = get_post( $args[0] );
		if ( empty( $post ) ) {
			$caps[] = 'do_not_allow';
			break;
		}

最后来看下/wp-admin/post.php,当get到数据是错误的,才会用post过来的数据

wp_reset_vars( array( 'action' ) );
if ( isset( $_GET['post'] ) )
 	$post_id = $post_ID = (int) $_GET['post'];
elseif ( isset( $_POST['post_ID'] ) )
 	$post_id = $post_ID = (int) $_POST['post_ID'];
else
 	$post_id = $post_ID = 0;

 
敲黑板了:又有个判断条件,current_user_can(‘edit’,$post_ID) ,当post_ID是正确的,才会执行上面我们分析的那个漏洞
有这几种情况:
get传入,get错了,才是post,假设post对了,执行了那个函数,但是没有触发漏洞(post上去的查询不为空)
get传入,get对了,执行了那个函数,再post置为查询为空,触发漏洞
。。。。

2.获取_wpnonce绕过CSRF防御

wordpress每一个功能操作都有token,有些(按照p师傅所说,只有一些是输出来的)是当前页面显示的,和我们爬虫登陆时要找hidden属性隐藏的auto_token一个道理
是要查上传的token的

case 'postajaxpost':
case 'post':
	check_admin_referer( 'add-' . $post_type );
	$post_id = 'postajaxpost' == $action ? edit_post() : write_post();
	redirect_post( $post_id );
	exit();

 
看wp_dashboard_quick_press函数(丢进草稿箱)

<?php wp_nonce_field( 'add-post' ); ?>

看下wp_nonce_field函数,发现是个输出隐藏属性的函数

function wp_nonce_field( $action = -1, $name = "_wpnonce", $referer = true , $echo = true ) {
	$name = esc_attr( $name );
	$nonce_field = '';
	if ( $referer )
		$nonce_field .= wp_referer_field( false );
	if ( $echo )
		echo $nonce_field;
	return $nonce_field;
}

 
到此,找到了token

3.竞争漏洞导致的逻辑漏洞

我们上面,post上去一个查询为空的数据,跳过了检查,下面这点就是利用
看一下绕过去了,接下来的代码操作
post.php里post_data函数来操作数据

			if ( ! is_array( $terms ) ) {
				$comma = _x( ',', 'tag delimiter' );
				if ( ',' !== $comma ) {
					$terms = str_replace( $comma, ',', $terms );
				}
				$terms = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) );
			}
			$clean_terms = array();
			foreach ( $terms as $term ) {
				// Empty terms are invalid input.
				if ( empty( $term ) ) {
					continue;
				}
				$_term = get_terms( $taxonomy, array(
					'name' => $term,
					'fields' => 'ids',
					'hide_empty' => false,
				) );
				if ( ! empty( $_term ) ) {
					$clean_terms[] = intval( $_term[0] );
				} else {
					// No existing term was found, so pass the string. A new term will be created.
					$clean_terms[] = $term;
				}
			}
			$post_data['tax_input'][ $taxonomy ] = $clean_terms;
		}
	}
	add_meta( $post_ID );
	update_post_meta( $post_ID, '_edit_last', get_current_user_id() );
	$success = wp_update_post( $post_data );

我跟了下get_terms函数,它还在套函数,就没有继续跟进了,是执行sql查询
最后再进行wp_update_post函数
查询到更新这段时间是真空的,也就是说造成了竞争关系
假设在这段时间内,有新插入的文章,那么我们之前那个“不存在”的id,不就可能可以存在了吗(只需要把id设置为最新一篇文章id+1)
存在怎么样,就执行update语句了,就可以插入文章了

4.untrash文章时造成的SQL注入漏洞

function get_post_meta_by_id( $mid ) {
	return get_metadata_by_mid( 'post', $mid );
}
	$statuses = get_post_meta($post_id, '_wp_trash_meta_comments_status', true);
	if ( empty($statuses) )
		return true;
	/**
	 * Fires before comments are restored for a post from the trash.
	 *
	 * @since 2.9.0
	 *
	 * @param int $post_id Post ID.
	 */
	do_action( 'untrash_post_comments', $post_id );
	// Restore each comment to its original status.
	$group_by_status = array();
	foreach ( $statuses as $comment_id => $comment_status )
		$group_by_status[$comment_status][] = $comment_id;
	foreach ( $group_by_status as $status => $comments ) {
		// Sanity check. This shouldn't happen.
		if ( 'post-trashed' == $status ) {
			$status = '0';
		}
		$comments_in = implode( ', ', array_map( 'intval', $comments ) );
		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->comments SET comment_approved = %s WHERE comment_ID IN ($comments_in)", $status ) );
	}

先用get_post_meta函数从数据库中取出meta,之后以字符串拼接的方式插入SQL语句。
这里数据就产生了数据库操作,且没有任何检查。
wp_trash_post函数删除文章

function wp_trash_post( $post_id = 0 ) {
	if ( !EMPTY_TRASH_DAYS )
		return wp_delete_post($post_id, true);
	if ( !$post = get_post($post_id, ARRAY_A) )
		return $post;
	if ( $post['post_status'] == 'trash' )
		return false;
	/**
	 * Fires before a post is sent to the trash.
	 *
	 * @since 3.3.0
	 *
	 * @param int $post_id Post ID.
	 */
	do_action( 'wp_trash_post', $post_id );
	add_post_meta($post_id,'_wp_trash_meta_status', $post['post_status']);
	add_post_meta($post_id,'_wp_trash_meta_time', time());
	$post['post_status'] = 'trash';
	wp_insert_post($post);
	wp_trash_post_comments($post_id);

wp_trash_post_comments  函数紧跟着删除评论

	// Set status for all comments to post-trashed.
	$result = $wpdb->update($wpdb->comments, array('comment_approved' => 'post-trashed'), array('comment_post_ID' => $post_id));
	clean_comment_cache( array_keys($statuses) );

是在进行sql操作,comment_approved
edit_comment修改评论函数里有这个名,可控数据(敲黑板了
5.
当你认真看完了前面的,再看流程图就豁然开朗了
利用快速草稿插入文章->越权编辑文章->插入评论->修改评论,(即comment_approved可控参数)(恶意数据入库)->删除文章(即上文提到的trash垃圾箱)(恶意数据进入另一个库)->反删除文章,即get_mata提出数据进行sql操作,并未进行数据的检查和判断(恶意数据被取出,直接插入SQL语句导致注入)
6.具体操作看原文作者文章

二.WordPress(4.8.2及以下版本) SQL注入漏洞利用

参考  http://www.freebuf.com/vuls/153959.html
在wp_update_post函数与wp_insert_post函数中,会从$post_data[‘meta_input’]中取出数据,不经检查直接添加到自定义栏目/字段中。
select * from wp_postmata where mata_key=’_edit_last’
再执行
update wp_postmeta set meta_key=caoncat(0x00,’_edit_last’) where meta_value=‘2’
再执行
select * from wp_postmata where mata_key=’_edit_last’
还是显示的出来数据
ok,详细看原文
 
 
2018.8.11

发表评论

电子邮件地址不会被公开。 必填项已用*标注