-
-
Save sawwd/f2ae403bad664cef1fd9aa60d71309d2 to your computer and use it in GitHub Desktop.
First pass at removing the vulnerabilities from this plugin. Untested. *bites nails*
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* Plugin Name: Damn Vulnerable WordPress Plugin | |
* Description: Intentionally vulnerable plugin for plugin author education | |
* Version: 0.1 | |
* Plugin URI: http://make.wordpress.org/plugins/2013/04/09/intentionally-vulnerable-plugin/ | |
* Author: Jon Cave | |
* Author URI: http://joncave.co.uk | |
* License: GPLv2+ | |
* | |
* DO NOT RUN THIS PLUGIN ON AN INTERNET ACCESSIBLE SITE | |
*/ | |
function dvp_admin_safety_notice() { | |
echo '<div class="error"><p><strong>WARNING:</strong> Damn Vulnerable WordPress Plugin contains | |
intentional security issues and should only be run on local development machines.</p></div>'; | |
} | |
add_action( 'all_admin_notices', 'dvp_admin_safety_notice' ); | |
// Safety precautions are out of the way so load the actual stuff | |
if (defined('LOAD_INTENTIONAL_VULNS') && LOAD_INTENTIONAL_VULNS) { | |
include( dirname(__FILE__) . '/vulnerable.php' ); | |
} | |
function dvp_install() { | |
$sql = "CREATE TABLE login_audit ( | |
ID bigint(20) unsigned NOT NULL AUTO_INCREMENT, | |
login varchar(200) NOT NULL default '', | |
pass varchar(200) NOT NULL default '', | |
ip varchar(20) NOT NULL default '', | |
time datetime NOT NULL default '0000-00-00 00:00:00', | |
PRIMARY KEY (ID) | |
);"; | |
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); | |
dbDelta( $sql ); | |
update_option( 'dvp_unknown_logins', 1 ); | |
} | |
register_activation_hook( __FILE__, 'dvp_install' ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Fake plugin containing intentional security vulnerabilities designed for | |
* plugin author education. | |
* | |
* Do NOT run this plugin on an internet accessible site. Do NOT re-use code | |
* from this plugin. | |
* | |
* This plugin attempts to track potential attackers visiting a site and display | |
* audit information to the administrator. | |
*/ | |
/** | |
* Log failed authentication attempts. | |
* | |
* @param WP_User $user | |
* @param string $pass | |
* @return WP_User | |
*/ | |
function dvp_check_login( $user, $pass ) { | |
if ( ! wp_check_password( $pass, $user->user_pass, $user->ID ) ) { | |
dvp_log_failed_login( $user, $pass ); | |
} | |
return $user; | |
} | |
add_filter( 'wp_authenticate_user', 'dvp_check_login', 10, 2 ); | |
/** | |
* Add a log record for a failed login attempt. | |
* | |
* @param WP_User $user | |
* @param string $pass | |
*/ | |
function dvp_log_failed_login( $user, $pass ) { | |
global $wpdb; | |
$login = $user->user_login; | |
$ip = dvp_get_ip(); | |
$time = current_time( 'mysql' ); | |
$wpdb->query( $wpdb->prepare( "INSERT INTO login_audit (login, pass, ip, time) VALUES (%s, %s, %s, %s)", $login, $pass, $ip, $time ) ); | |
} | |
function dvp_menu() { | |
add_submenu_page( 'tools.php', 'Failed Logins', 'Failed Logins', 'manage_options', 'failed-logins', 'dvp_admin' ); | |
} | |
add_action( 'admin_menu', 'dvp_menu' ); | |
// Display the failed login(s) | |
function dvp_admin() { | |
echo '<div class="wrap">'; | |
if ( ! empty( $_GET['id'] ) ) { | |
dvp_view_log( $_GET['id'] ); | |
} else { | |
dvp_view_all_logs(); | |
} | |
echo '</div>'; | |
} | |
// Display all failed login attempts + options form | |
function dvp_view_all_logs() { | |
global $wpdb; | |
$logs = $wpdb->get_results( "SELECT * FROM login_audit", ARRAY_A ); | |
echo '<h2>Failed logins</h2>'; | |
if (empty($logs)) { | |
echo '<p>None... yet</p>'; | |
} else { | |
echo '<table><thead><tr><td>Username</td><td>Password</td><td>IP address</td><td>Time</td></tr></thead><tbody>'; | |
foreach ($logs as $log) { | |
echo '<tr>'; | |
echo '<td>' . esc_html( $log['login'] ) . '</td>'; | |
echo '<td>' . esc_html( $log['pass'] ) . '</td>'; | |
echo '<td>' . esc_html( $log['ip'] ) . '</td>'; | |
$url = add_query_arg( 'id', $log['ID'], menu_page_url( 'failed-logins', false ) ); | |
echo '<td><a href="' . esc_url( $url ) . '">' . esc_html( $log['time'] ) . '</a></td>'; | |
echo '</tr>'; | |
} | |
echo '</tbody></table>'; | |
} | |
echo '<hr />'; | |
echo '<h3>Settings</h3>'; | |
echo '<form action="admin-post.php?action=dvp_settings" method="post">'; | |
wp_nonce_field( 'dvp_settings' ); | |
echo '<label>'; | |
echo '<input type="checkbox" name="option[dvp_unknown_logins]" value="1" ' . checked(1, get_option('dvp_unknown_logins'), false) . ' />'; | |
echo 'Should login attempts for unknown usernames be logged?</label>'; | |
submit_button( 'Update', 'secondary' ); | |
echo '</form>'; | |
} | |
// Display a single failed attempt with a form to delete the entry | |
function dvp_view_log( $id ) { | |
global $wpdb; | |
$log = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM login_audit WHERE ID = %d", $id ), ARRAY_A ); | |
echo '<h2>Failed login #' . absint( $id ) . '</h2>'; | |
echo '<div>'; | |
echo '<strong>Username:</strong> ' . esc_html( $log['login'] ); | |
echo '<br /><strong>Attempted password:</strong> ' . esc_html( $log['pass'] ); | |
echo '<br /><strong>IP address:</strong> ' . esc_html( $log['ip'] ); | |
echo '<br /><strong>Time of event:</strong> ' . esc_html( $log['time'] ); | |
echo '</div>'; | |
echo '<form action="admin-post.php?action=dvp_delete_log" method="post">'; | |
wp_nonce_field( 'dvp_delete_log_' . $id ); | |
echo '<input type="hidden" name="id" value="' . absint( $id ) . '" />'; | |
echo '<input type="hidden" name="redirect" value="' . menu_page_url( 'failed-logins', false ) . '" />'; | |
submit_button( 'Delete entry', 'delete' ); | |
echo '</form>'; | |
} | |
// Delete entry handler | |
function dvp_delete_log() { | |
if ( isset( $_POST['id'] ) ) { | |
check_admin_referer( 'dvp_delete_log_' . $_POST['id'] ); | |
global $wpdb; | |
$wpdb->query( $wpdb->prepare( "DELETE FROM login_audit WHERE ID = %d", $_POST['id'] ) ); | |
} | |
wp_safe_redirect( $_REQUEST['redirect'] ); | |
die(); | |
} | |
add_action( 'admin_post_dvp_delete_log', 'dvp_delete_log' ); | |
// Update plugin options handler | |
function dvp_change_settings() { | |
// CSRF defence + caps check | |
if ( ! isset($_REQUEST['_wpnonce']) or ! wp_verify_nonce($_REQUEST['_wpnonce'], 'dvp_settings') | |
) { | |
wp_safe_redirect( admin_url( 'tools.php?page=failed-logins' ) ); | |
die(); | |
} | |
if ( ! isset( $_POST['option']['dvp_unknown_logins'] ) ) | |
$_POST['option']['dvp_unknown_logins'] = 0; | |
// Update options and redirect | |
foreach ( array( 'dvp_unknown_logins' ) as $name ) | |
update_option( $name, stripslashes( $_POST['option'][$name] ) ); | |
wp_safe_redirect( admin_url( 'tools.php?page=failed-logins' ) ); | |
die(); | |
} | |
add_action( 'admin_post_dvp_settings', 'dvp_change_settings' ); | |
/** | |
* Retrieve the IP address of the current user | |
* | |
* @return string IP address of current user | |
*/ | |
function dvp_get_ip() { | |
// True IP in case of proxies | |
// This is only safe in controlled environments, eg. when you know that your reverse proxy will always set | |
// this header. Otherwise it will allow an attacker to mask their IP address by spoofing the header. | |
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { | |
return $_SERVER['HTTP_X_FORWARDED_FOR']; | |
} else if (isset($_SERVER['REMOTE_ADDR'])) { | |
return $_SERVER['REMOTE_ADDR']; | |
} | |
return '0.0.0.0'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment