CVE-2020-600/6009/6010/11511:在线学习平台多安全漏洞

2020-05-14 约 525 字 预计阅读 3 分钟

声明:本文 【CVE-2020-600/6009/6010/11511:在线学习平台多安全漏洞】 由作者 angel010 于 2020-05-14 08:09:17 首发 先知社区 曾经 浏览数 37 次

感谢 angel010 的辛苦付出!

漏洞概述

研究人员在最常见的Learning Management Systems(LMS)插件LearnPress、LearnDash和LifterLMS中发现了多个安全漏洞,包括权限提升漏洞、SQL注入、远程代码执行漏洞。

研究人员共发现了4个漏洞,分别是CVE-2020-6008、CVE-2020-6009、CVE-2020-6010和CVE-2020-11511。攻击者利用这些漏洞可以让学生甚至非认证的用户获取敏感细腻些、编辑个人记录,甚至控制LMS平台。

漏洞分析

LearnPress漏洞

是WordPress最主流的LMS插件,可以让网站管理员很容易地创建和销售在线课程。据BuiltWith消息,LearnPress是在线学习管理系统中排名第二的互联网平台,安装量超过24000。据WordPress插件网站官方消息,其安装量超过80000+,开发插件的公司称该插件在超过21000所学校使用。漏洞影响版本号低于3.2.6.7的LearnPress版本。

CVE-2020-6010: SQL注入

该漏洞是一个根据时间的盲注入漏洞,很难发现和利用。类LP_Modal_Search_Items的方法_get_items存在SQL注入漏洞。该方法在将用户提供的数据作为SQL查询之前没有进行正则处理。经过认证的用户可以调用Ajax方法learnpress_modal_search_items来触发该漏洞,该方法会执行以下链:

LP_Admin_Ajax::modal_search_items → LP_Modal_Search_Items::get_items →
LP_Modal_Search_Items::_get_items.

CVE-2020-11511: 权限提升为教师

该漏洞是一个继承性质的权限提升漏洞。
函数learn_press_accept_become_a_teacher可以用来将一个注册的用户升级为teacher角色,导致权限提升。由于代码没有检查请求用户的权限,因此任意的学生角色都可以调用该函数:

function learn_press_accept_become_a_teacher() {
   $action  = ! empty( $_REQUEST['action'] ) ? $_REQUEST['action'] : '';
   $user_id = ! empty( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : '';
   if ( ! $action || ! $user_id || ( $action != 'accept-to-be-teacher' ) ) {
       return;
   }
   if ( ! learn_press_user_maybe_is_a_teacher( $user_id ) ) {
       $be_teacher = new WP_User( $user_id );
       $be_teacher->set_role( LP_TEACHER_ROLE );
       delete_transient( 'learn_press_become_teacher_sent_' . $user_id );
       do_action( 'learn_press_user_become_a_teacher', $user_id );
       $redirect = add_query_arg( 'become-a-teacher-accepted', 'yes' );
       $redirect = remove_query_arg( 'action', $redirect );
       wp_redirect( $redirect );
   }
}
add_action( 'plugins_loaded', 'learn_press_accept_become_a_teacher' );
...

该函数在激活的插件加载时就会被调用,也就是说提供action和user_id参数给/wpadmin/就可以被调用,甚至无需登入。
研究人员发现Wordfence也发现了该漏洞。

LearnDash漏洞

LearnDash是一个常用的WordPress LMS插件。据BuiltWith,目前有超过33000个网站运行着LearnDash。LearnDash广泛用于财富五百强公司和一些顶尖大学。漏洞影响版本号低于3.1.6的LearnDash版本。

CVE-2020-6009:非认证的二阶SQL注入

该漏洞与其他SQL注入漏洞一样,通过使用预先准备的statement可以预防。漏洞位于ld-groups.php文件中的learndash_get_course_groups函数。该函数没有对用户提供的用作SQL查询的数据进行正则化处理。该漏洞可以在没有认证的情况下触发。

有漏洞的函数learndash_get_course_groups如下所示:

function learndash_get_course_groups( $course_id = 0, $bypass_transient = false ) {
...
        $sql_str = $wpdb->prepare("SELECT DISTINCT REPLACE(meta_key, 'learndash_group_enrolled_', '') FROM ". $wpdb->postmeta ." WHERE meta_key LIKE %s AND post_id = %d and meta_value != ''", 'learndash_group_enrolled_%', $course_id );
...
        $col = $wpdb->get_col( $sql_str );
...
           $sql_str = "SELECT ID FROM $wpdb->posts WHERE post_type='groups' AND post_status = 'publish' AND ID IN (" . implode( ',', $col ) . ')';
           $course_groups_ids = $wpdb->get_col( $sql_str );
...

函数查询表wp_postmeta来对特定的post ($course_id)查询learndash_groupenrolled%的meta_keys。然后移除learndash_groupenrolled prefix,并将剩余值用于另一个SQL查询中。也就是说如果可以找到一种方式插入恶意记录到wp_postmeta 中,就可以控制meta_key的值,也就实现了SQL 注入。
此外,负责处理IPN 交易的ipn.php文件中含有以下代码:

...
// log transaction
ld_ipn_debug( 'Starting Transaction Creation.' );
$transaction = $_REQUEST; // <--- controlled input!
$transaction['user_id'] = $user_id;
$transaction['course_id'] = $course_id; // <--- controlled input!
$transaction['log_file'] = basename($ipn_log_filename);
$course_title = '';
$course       = get_post( $course_id );
if ( ! empty( $course) ) {
   $course_title = $course->post_title;
}
ld_ipn_debug( 'Course Title: ' . $course_title );
$post_id = wp_insert_post( array('post_title' => "Course {$course_title} Purchased By {$email}", 'post_type' => 'sfwd-transactions', 'post_status' => 'publish', 'post_author' => $user_id) );
ld_ipn_debug( 'Created Transaction. Post Id: ' . $post_id );
foreach ( $transaction as $k => $v ) {
   update_post_meta( $post_id, $k, $v ); // <--- this creates the malicious post meta that is later queried and used in another SQL query.
}
...

PayPal IPN是PayPal通知网站PayPal交易的方式。
PayPal会发送一个含有关于交易信息的POST请求到网站的IPN监听器。请求中的数据会用verify_sign参数来签名。
网站的IPN监听器会用HTTPS发送完整的消息回PayPal,添加参数cmd=_notify-validate
PayPal会响应VERIFIEDINVALID
然后就可以读取PayPal IPN。PayPal的 IPN模拟器可以在沙箱环境中构造IPN请求。
ipn.php中的代码可以允许攻击者用meta_keymeta_valuewp_postmeta表中创建一条记录,之后调用learndash_get_course_groups来利用SQL注入。

可以在wp_postmeta中插入一条恶意记录,因此需要找到一种调用learndash_get_course_groups的方法。研究人员想到了ipn.php文件。

ipn.php可以通过以下调用链来调用learndash_get_course_groups

ipn.php → ld_update_course_access → learndash_update_course_users_groups → learndash_get_course_groups

利用该漏洞的第一步是需要使用IPN Simulator来伪造一个有效的IPN请求。对ipn.php中的$course_id,研究人员使用ID=1。然后,用以下参数扩展IPN请求:

debug=1&learndash_group_enrolled_1)INJECTION_POINT%23=1

在该请求中需要考虑3点:

debug=1会引发路径/wp-content/uploads/learndash/paypal_ipn/中的一个日志文件创建,该路径中含有一个含有恶意meta_key的新post。日志文件名是一个简单的time().log,可以通过枚举来猜测。

一般来说,注入发生在key=valuevalue部分,但是本例中的注入发生在key=valuekey部分。而且无法使用空格,因为PHP在访问$_REQUEST时会替换空格。但是可以使用/**/来替代空格,这是key部分的一种有效字符,在SQL查询中也是空格的一种有效替换,比如SELECT 1SELECT/**/1是等价的。
研究人员将%23加到了注入点的后面,这是#的编码表示。然后需要使用post id,发送含有item_number=post_id的IPN请求,这会触发SQL注入。

整个查询的执行如下所示:

SELECT ID FROM wp_posts WHERE post_type='groups' AND post_status = 'publish' AND ID IN (1)[MALICIOUS SQL QUERY]#)

LifterLMS漏洞分析

LifterLMS是另外一个常用的 LMS WordPress插件。根据BuiltWith的统计,大约有17000个网站使用该插件,包括学校和教育机构。

CVE-2020-6008: 任意文件写漏洞

该漏洞是一个任意文件写漏洞,利用了PHP的动态特征/因为WordPress可以让插件注册新的action到admin-ajax handler,LifterLMS可以注入自己的handle()函数:

public static function handle() {
   // Make sure we are getting a valid AJAX request
   check_ajax_referer( self::NONCE );
   // $request = self::scrub_request( $_REQUEST );
   $request = $_REQUEST;
   $response = call_user_func( 'LLMS_AJAX_Handler::' . $request['action'], $request );
...

该函数会根据sent action变量调用LLMS_AJAX_Handlercall_user_funcLLMS_AJAX_Handler中一个可用的函数就是export_admin_table:

public static function export_admin_table( $request ) {
   require_once 'admin/reporting/class.llms.admin.reporting.php';
   LLMS_Admin_Reporting::includes();
   $handler = 'LLMS_Table_' . $request['handler'];
   if ( class_exists( $handler ) ) {
      $table = new $handler();
      $file  = isset( $request['filename'] ) ? $request['filename'] : null;
      return $table->generate_export_file( $request, $file );
...

该函数首先会根据发送的handler变量来创建一个新的类,然后用请求中发送的filename变量来调用generate_export_file函数。generate_export_file是一个继承函数,应该根据用handler变量创建的对应LLMS_Table类的信息来创建一个CSV文件。

但是代码没有成功验证filename变量中的扩展。原因是没有发送$type,因为默认是CSV

public function generate_export_file( $args = array(), $filename = null, $type = 'csv' ) {
   if ( 'csv' !== $type ) {
      return false;
   }
..
$file_path   = LLMS_TMP_DIR . $filename;
…
$handle = @fopen( $file_path, 'a+' );
...

此时,攻击者可以拦截一个标准的Ajax请求,使用ajax_nonce变量来访问generate_export_file
该漏洞使得攻击者可以写任意的PHP文件,但无法控制任何内容。研究人员发现有一个可以使用的LLMS_TablesLLMS_Tables_Course_Students
注册的学生可以检查注册了哪些courses id,然后在Ajax请求中发送。然后可以将名字输出到生成的文件中。
用户可以到profile页,并修改姓到期望的PHP代码,比如TEST <?php phpinfo(); /*
WordPress输入过滤器机制并没有在相同的输入中考虑< >,但是可以在文件中使用任意的<,然后再其他部分使用注释符号/*
这样,只要浏览生成的PHP文件就会执行用户的姓中写的PHP代码,实现服务器上的代码执行。

漏洞影响

  • 窃取注册用户的个人信息,如用户名、邮件、姓名、密码等。
  • 因为平台中包含支付信息,因此攻击者会在无需管理员信息的情况下修改网站。
  • 学生相关:
    修改自己和别人的成绩;
    伪造证书;
    提前获取考试信息;
    获取考试答案;
    提升权限为教师角色。

https://research.checkpoint.com/2020/e-learning-platforms-getting-schooled-multiple-vulnerabilities-in-wordpress-most-popular-learning-management-system-plugins/

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now