Hace un par de días, mientras hacia unas pruebas y revisaba unos sistemas descubrí una vulnerabilidad que afectaba a unos cuantos plugins de Wordpress instalados.
Continuando mi investigación llegúe a la conclusión de que se trata de una vulnerabilidad Full Path Disclosure (FPD) que afecta a la mayoría de los plugins de Wordpress, incluyendo al "Hello Dolly" y Akismet (plugins por defecto). Para explotar esta vulnerabilidad, no es necesario que el plugin esté activo, simplemente que esté instalado. Esta vulnerabilidad es debido a un problema o error a la hora de programar los plugins, al no validar la existencia de funciones antes de ejecutarlas el sistema devuelve un error fatal, mostrandonos la ruta completa de la instalación del CMS. Por ejemplo, en Akismet:
Fatal error: Call to undefined function add_action() in /home/XXYYZZ/public_html/wp-content/plugins/akismet/akismet.php on line 26
Hablando un poco sobre la vulnerabildiad FPD y recordando lo que dije en el post pasado, explotar esta vulnerabilidad no significa que podamos hacer grandes cosas, simplemente nos entregará información que podría ser utilizada para lo que uno estime conveniente.
Analizando más profundamente el código y el error, podemos encontrar (siguiendo con Akismet) las siguientes líneas:
< ?php
function akismet_init() {
global $wpcom_api_key, $akismet_api_host, $akismet_api_port;
if ( $wpcom_api_key )
$akismet_api_host = $wpcom_api_key . '.rest.akismet.com';
else
$akismet_api_host = get_option('wordpress_api_key') . '.rest.akismet.com';
$akismet_api_port = 80;
add_action('admin_menu', 'akismet_config_page');
add_action('admin_menu', 'akismet_stats_page');
akismet_admin_warnings();
}
add_action('init', 'akismet_init');
function akismet_admin_init() {
if ( function_exists( 'get_plugin_page_hook' ) )
$hook = get_plugin_page_hook( 'akismet-stats-display', 'index.php' );
else
$hook = 'dashboard_page_akismet-stats-display';
add_action('admin_head-'.$hook, 'akismet_stats_script');
}
add_action('admin_init', 'akismet_admin_init');
?>Sólo en esas líneas se hace unas cuantas veces la llamada a la función "add_action()" que no es validada antes de ser ejecutada, sin embargo, otras funciones si lo están (por ej. mirar línea 18). Lo mismo sucede en hello.php y en muchos otros plugins muy usados.
Para detectar esta vulnerabilidad los plugins que tenemos instalados podemos ejecutar el siguiente comando dentro del directorio wp-content/plugins:
find . -name "*.php" -exec grep -H add_action {} \;
Y obtendremos un resultado como el siguiente:
./wp-gravatar/gravatars.php:add_action('admin_menu', 'gravatar_admin_menu'); ./wp-gravatar/gravatars.php:add_action('plugins_loaded', 'widget_authdescription_init'); ./wp-gravatar/gravatars.php:add_action('plugins_loaded', 'widget_recent_comments_gravatars_register', 1); ./smart-youtube/smartyoutube.php:add_action('admin_menu', 'yte_add_pages'); ./smart-youtube/smartyoutube.php:add_action( 'plugins_loaded', 'yte_install' ); ./smart-youtube/smartyoutube.php:add_action( 'after_plugin_row', 'yte_check_plugin_version' ); ./chili-code-highlighter/chili-code-highlighter.php:add_action( 'plugins_loaded', create_function( '', 'global $ChiliCodeHighlighter; $ChiliCodeHighlighter = new ChiliCodeHighlighter();' ) ); ./wp-db-backup/wp-db-backup.php:add_action('plugins_loaded', 'wpdbBackup_init'); ./subscribe2/counterwidget.php:add_action('plugins_loaded', 'widget_s2counter_init'); ./add-to-any/add-to-any.php:add_action('init', 'A2A_SHARE_SAVE_textdomain'); ./add-to-any/add-to-any.php:add_action('the_content', 'A2A_SHARE_SAVE_to_bottom_of_content', 98); ./add-to-any/add-to-any.php:add_action('wp_head', 'A2A_SHARE_SAVE_button_css'); ./add-to-any/add-to-any.php:add_action('admin_head', 'A2A_SHARE_SAVE_admin_head'); ./add-to-any/add-to-any.php:add_action('admin_menu', 'A2A_SHARE_SAVE_add_menu_link'); ./creative-commons-license-widget/ccLicense.php:add_action('widgets_init', 'widget_ccLicense_init'); ./wordpress-23-related-posts-plugin/wp_related_posts.php:add_action('init', 'init_textdomain'); ./wordpress-23-related-posts-plugin/wp_related_posts.php:add_action('admin_menu', 'wp_add_related_posts_options_page'); ./twitter-updater-using-tinyurl/twitter_updater.php:add_action ( 'save_post', 'vc_twit'); ./twitter-updater-using-tinyurl/twitter_updater.php:add_action('admin_menu', 'vc_addTwitterAdminPages'); ./simplepie-core/simplepie_core.php:add_action('admin_menu', 'simplepie_core_options'); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('edit_post', array($aiosp, 'post_meta_tags')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('publish_post', array($aiosp, 'post_meta_tags')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('save_post', array($aiosp, 'post_meta_tags')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('edit_page_form', array($aiosp, 'post_meta_tags')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('init', array($aiosp, 'init')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('wp_head', array($aiosp, 'wp_head')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('template_redirect', array($aiosp, 'template_redirect')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('admin_menu', array($aiosp, 'admin_menu')); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action('admin_menu', 'aiosp_meta_box_add'); ./all-in-one-seo-pack/all_in_one_seo_pack.php:add_action( 'admin_notices', 'aioseop_activation_notice'); ./google-analyticator/google-analyticator.php:add_action('admin_init', 'ga_admin_init'); ./google-analyticator/google-analyticator.php:add_action('admin_menu', 'add_ga_option_page'); ./google-analyticator/google-analyticator.php:add_action('init', 'ga_outgoing_links'); ./google-analyticator/google-analyticator.php:add_action('plugin_action_links_' . plugin_basename(__FILE__), 'ga_filter_plugin_actions'); ./google-analyticator/google-analyticator.php:add_action('wp_ajax_ga_ajax_accounts', 'ga_ajax_accounts'); ./google-analyticator/google-analytics-stats-widget.php:add_action('widgets_init', 'GoogleStatsWidget_init'); ./akismet/akismet.php:add_action('init', 'akismet_init'); ./akismet/akismet.php:add_action('admin_init', 'akismet_admin_init'); ./akismet/akismet.php:add_action('wp_set_comment_status', 'akismet_submit_spam_comment'); ./akismet/akismet.php:add_action('edit_comment', 'akismet_submit_spam_comment'); ./akismet/akismet.php:add_action('preprocess_comment', 'akismet_auto_check_comment', 1); ./akismet/akismet.php:add_action('admin_menu', 'akismet_manage_page'); ./akismet/akismet.php:add_action('activity_box_end', 'akismet_stats'); ./akismet/akismet.php:add_action('rightnow_end', 'akismet_rightnow'); ./akismet/akismet.php:add_action('manage_comments_nav', 'akismet_check_for_spam_button'); ./akismet/akismet.php:add_action('admin_action_akismet_recheck_queue', 'akismet_recheck_queue'); ./akismet/akismet.php:add_action('init', 'widget_akismet_register'); ./source-code-syntax-highlighting-plugin-for-wordpress/deans_code_highlighter.php:add_action('admin_menu', 'dl_reg_admin'); ./flickr-widget/flickr_widget.php:add_action( "init", "flickr_widgets_init" );
La lista de plugins afectados es muy grande, sólo en este caso (mi wordpress) tenemos 15:
wp-gravatar smart-youtube chili-code-highlighter wp-db-backup subscribe2 add-to-any creative-commons-license-widget wordpress-23-related-posts-plugin twitter-updater-using-tinyurl simplepie-core all-in-one-seo-pack google-analyticator akismet source-code-syntax-highlighting-plugin-for-wordpress flickr-widget
Para corregirlo simplemente debemos agregar, a cada llamada de la función, la siguiente validación:
if(function_exists('add_action')) {
[...]
}Quizás a mucha gente no le interese este tema y piense que este tipo de vulnerabilidad no sirve y no les importa que sepan en que directorio está instalado el wordpress, aún así, pienso que es necesario que esto se corrija. No va a depender de los usuarios solucionar esto, sino de los desarrolladores.
Esta vulnerabilidad puede ser aprovechada no sólo para entrar a un sistema wordpress, puede ser combinada con otras vulnerabildiades como LFI o Directory Transversal y comprometer a las otras cuentas que estan en el servidor. Con esta información podemos obtener típicamente el nombre de usuario de la cuenta (luego ingresar por la IP agregando colita de chancho [~] y luego el username) y el directorio de ese usuario, que luego pueden ser usados para distintos fines.
Las personas que alguna véz hayan explotado este tipo de vulnerabilidad sabrán lo útil que puede llegar a ser al momento de querer penetrar un sistema.
Si lo miramos desde otro punto de vista, podríamos llegar a escalar el problema y decir que gran parte de la responsabilidad la tiene WordPress, todos los plugins deberían pasar por un test de calidad donde se verifiquen este tipo de cosas. La forma de operar que tienen los plugins de WordPress deberían ser, al menos, en un entorno WP. ¿Me explico? Debería existir una forma de determar que el fichero php se está ejecutando bajo el entorno wordpress y no por si solo (por ejemplo al llamar a hello.php), ya sea con un método, constante, variable, etc, algo tan simple como un if: