jump Index ScientificPsychic.com
Scientific Psychic

Spammers and hackers target WordPress web sites

If you have a WordPress blog, you have probably been the target of spam attacks. Spammers hope to get more visitors or increase their page rank by putting links to their web sites on the comment fields of your blog articles. Various plugins can be used to block WordPress spam, including Akismet, wp-reCaptcha, si-captcha-for-wordpress, and others. However, even with the captcha methods that require a user to type a matching word to prove that he or she is not a spambot, the spam gets through. How is this possible? The problem is that wp-comments-post.php, which is a core module of WordPress for processing comments, provides an action hook for pre-processing comments that is exploited by spammers. A hack targeted toward a WordPress theme, such as the following, adds filters that make it possible to add comments programatically without going through the comment dialog of the blog and any captchas that may have been established to reduce spam.

/* Override WordPress comment dialog processing by setting filters  */
function ph_set_bypass_filters( $comment_post_ID )
{
    add_filter( 'pre_comment_approved', 'true' );
    ...
}
add_action( 'pre_comment_on_post', 'ph_set_bypass_filters', 10, 1 );

It is possible to prevent the hack above by modifying the wp-comments-post.php to disallow the use of action hooks for comment handling. Eliminating the following line from the code, as illustrated, prevents hackers and spammers from sending comments directly and forces all users to use the WordPress comment dialog.

...
if ( !comments_open($comment_post_ID) ) {
	do_action('comment_closed', $comment_post_ID);
	wp_die( __('Sorry, comments are closed for this item.') );
} elseif ( 'trash' == $status ) {
	do_action('comment_on_trash', $comment_post_ID);
	exit;
} elseif ( !$status_obj->public && !$status_obj->private ) {
	do_action('comment_on_draft', $comment_post_ID);
	exit;
} elseif ( post_password_required($comment_post_ID) ) {
	do_action('comment_on_password_protected', $comment_post_ID);
	exit;
} else {
// * * * * * * * * * * * * * * * * * * * * * * * * *
// ****  This code allows sending comments for any blog post programmatically 
// ****  Disable the function by making it a comment with two slashes 

//	do_action('pre_comment_on_post', $comment_post_ID);

// * * * * * * * * * * * * * * * * * * * * * * * * *
}
...

The following is a discussion about a previous problem in wp-comments-post.php. After making an entry to my WordPress blog, I noticed that my RSS subscription service did not list the new entry for several days. When I examined the server files, I found out that my blog had been hacked. The wp-blog-header.php had been replaced, and the hacked header file was designed to redirect referrals from search engines to other web sites. Various WordPress sources recommended updating to the latest version of the software, but some people reported being hacked even after updating. The new WordPress code and the anti-spam plugins like Akismet and image captcha were not preventing this problem.

Analyzing the WordPress code, it seemed that the hacking was made possible through buffer overflows because the code did not validate the comment data before using it. Even the "comment blacklist" specified through the WordPress administration panel was invoked by options-discussion.php only after a potentially harmful comment had been processed by the compact() subroutine.

I modified wp-comments-post.php to analyze the raw comment data and go to an error page when anything was invalid or unacceptable. My error page has some counters that let me monitor hacking attempts. Below is a listing of wp-comments-post.php with an example of the code that I inserted. The code restricts the length of the comments, and checks the comment contents, author names, author IP, and author domain. Since I implemented this code, I have monitored several hacking attempts that have been thwarted. You can customize your own blog code using these basic ideas. The comment validation can be extended further by performing a basic linguistic analysis to verify that the average word length of the comments and the ratios of function words like prepositions, articles, and conjuctions are within acceptable ranges for the language of the blog. In English, for example, the 10 most frequent words (the, be, of, and, a, in, he, to have, it) account for about 25% of the text.

The latest version of WordPress has fixed the problem with hacking through comment overflows, but the following code still is applicable for cases when you want to take specific action for particular types of comments.

Modifications to wp-comments-post.php
 ...
$comment_author       = trim($_POST['author']);
$comment_author_email = trim($_POST['email']);
$comment_author_url   = trim($_POST['url']);
$comment_content      = trim($_POST['comment']);

// If the user is logged in
get_currentuserinfo();
if ( $user_ID ) :
	$comment_author       = $wpdb->escape($user_identity);
	$comment_author_email = $wpdb->escape($user_email);
	$comment_author_url   = $wpdb->escape($user_url);
else :
	if ( get_option('comment_registration') )
		die( __('Sorry, you must be logged in to post a comment.') );
endif;

$comment_type = '';

if ( get_settings('require_name_email') && !$user_ID ) {
	if ( 6 > strlen($comment_author_email) || '' == $comment_author )
		die( __('Error: please fill the required fields (name, email).') );
	elseif ( !is_email($comment_author_email))
		die( __('Error: please enter a valid email address.') );
}

if ( '' == $comment_content )
	die( __('Error: please type a comment.') );

// **************************************************
// **** BEGIN  custom check to prevent hacking  *****
// **************************************************
if ( strlen($comment_content) > 2000 ) { // Reject very long comments
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
if ( strlen($comment_content) < 20  ) { // Reject very short comments
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
if ( stristr($comment_content, "transsexual") ) { // Reject comments with specific words
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
if ( stristr($comment_author, "transsexual") ) { // Reject comments from specific authors
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
$posty_ip = $_SERVER['REMOTE_ADDR']; // Get IP address
if ( $posty_ip == "78.46.198.162" ) { // Reject comments from specific IP addresses
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
$comment = get_comment($comment_id);
// Reject comments from authors with specific IP addresses
if ( stristr($comment->comment_author_IP, "78.46.88.142") ) {	
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
$comment_author_domain = gethostbyaddr($comment->comment_author_IP);
// Reject comments from specific domains
if ( stristr($comment_author_domain, "your-server.de") ) {	
	wp_redirect('http://www.website.com/errorpage.html',301); die(); 
}
// **************************************************
// ****  END  custom check to prevent hacking  *****
// **************************************************

$commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'user_ID');
$comment_id = wp_new_comment( $commentdata );

if ( !$user_ID ) :
	$comment = get_comment($comment_id);
	setcookie('comment_author_' . COOKIEHASH, $comment->comment_author, time() + 30000000, COOKIEPATH, COOKIE_DOMAIN);
	setcookie('comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + 30000000, COOKIEPATH, COOKIE_DOMAIN);
	setcookie('comment_author_url_' . COOKIEHASH, clean_url($comment->comment_author_url), time() + 30000000, COOKIEPATH, COOKIE_DOMAIN);
endif;
 ...


© Copyright  - Antonio Zamora