ThinkPHP框架基础
- 目录结构
thinkphp/
├── application/ # 应用目录(业务代码)
│ ├── index/ # 模块目录
│ │ ├── controller/ # 控制器 ⚠️ 审计重点
│ │ ├── model/ # 模型
│ │ └── view/ # 视图
│ ├── common.php # 公共函数 ⚠️ 审计重点
│ ├── config.php # 配置文件 ⚠️ 审计重点
│ └── route.php # 路由配置
├── thinkphp/ # 框架核心目录
│ ├── library/ # 核心类库
│ │ └── think/ # Think命名空间
│ ├── start.php # 启动文件
│ └── base.php # 基础定义
├── public/ # Web根目录
│ └── index.php # 入口文件 ⚠️ 审计起点
└── vendor/ # Composer依赖
2. 请求流程
-
- 入口文件 public/index.php
require __DIR__ . '/../thinkphp/base.php';
- 2. 启动流程
用户请求
↓
入口文件 // public/index.php
↓
框架初始化
↓
Route::check() // 路由解析
↓
Controller::exec() // 控制器调度
↓
Model::query() // 数据库操作
↓
View::fetch() // 模板渲染
3. 配置文件
// 应用状态(生产环境必须设为true)
'app_debug' => false,
// 禁止自动生成控制器
'auto_search' => false,
// 输入数据过滤
'default_filter' => 'htmlspecialchars',
// URL模式
'url_common_param' => false,
4. URL设计
- 普通模式
http://domain.com/index.php?s=/模块/控制器/操作/参数名/参数值
- 默认模式
http://domain.com/index.php/模块/控制器/操作/参数名/参数值
- 兼容模式
http://domain.com/index.php?s=/模块/控制器/操作
- 示例
http://www.example.com/index.php/index/user/login/id/10
模块: index
控制器: User
操作: login
参数: id=10
5. MVC模式
- MVC=Model(模型)+View(视图)+Controller(控制器)
- 请求流程
┌──────────────────────────────────────────┐
│ 用户请求 |
└─────────────────┬────────────────────────┘
↓
┌───────────────┐
│ Controller │ ← 处理业务逻辑
│ (控制器) |
└───────┬───────┘
│
┌────────┴────────┐
↓ ↓
┌─────────┐ ┌─────────┐
│ Model │ │ View │
│ (模型) │←─────→│ (视图) │
└─────────┘ └─────────┘
数据交互 页面展示
- 目录结构
project/
├── app/ # 模块
│ ├── controller/ # 控制器目录 (C)
│ │ ├── Index.php
│ │ └── User.php
│ │
│ ├── model/ # 模型目录 (M)
│ │ ├── User.php
│ │ └── Article.php
│ │
│ ├── view/ # 视图目录 (V)
│ │ ├── index/
│ │ │ └── index.html
│ │ └── user/
│ │ ├── login.html
│ │ └── profile.html
│ │
│ ├── middleware/ # 中间件
│ ├── validate/ # 验证器
│ └── common.php # 公共函数
│
├── config/ # 配置目录
├── route/ # 路由定义
├── public/ # 入口目录
│ └── index.php
└── vendor/ # Composer依赖
- MVC
-
- Controller(控制器)
-
-
- 作用:接受用户请求;调用模型处理数据;加载视图返回响应。
- 示例
-
public function index()
{
// 1. 获取请求参数
$page = input('page', 1);
// 2. 调用模型获取数据
$userModel = new UserModel();
$users = $userModel->paginate(10);
// 3. 渲染视图
return View::fetch('index');
}
-
- Model(模型)
-
-
- 作用:封装数据库操作、定义数据关心、实现业务逻辑
-
-
- View(视图)
-
-
- 作用:模板渲染,前端界面输出
-
- 获取输入
-
- 助手函数
// 获取所有请求参数
$data = input();
// 获取指定参数
$name = input('name');
// 获取参数并设置默认值
$page = input('page', 1);
// 获取参数并指定类型
$id = input('id/d'); // 整数
$price = input('price/f'); // 浮点数
$name = input('name/s'); // 字符串
-
- 变量修饰符
// 类型转换
input('id/d'); // 整数 (digit)
input('price/f'); // 浮点数 (float)
input('name/s'); // 字符串 (string)
input('flag/b'); // 布尔值 (boolean)
input('ids/a'); // 数组 (array)
// 多个修饰符组合
input('email/s'); // 字符串并过滤
input('content/s'); // 获取字符串内容
-
- 使用Request获取
变量类型方法('变量名/变量修饰符','默认值','过滤方法')
// 助手函数
$request = request();
$name = $request->param('name');
- 数据库
-
- 基本查询
// 查询所有数据
$users = Db::table('user')->select();
// 查询单条数据
$user = Db::table('user')->where('id', 1)->find();
// 查询某个字段的值
$username = Db::table('user')->where('id', 1)->value('username');
// 查询某一列的值
$usernames = Db::table('user')->column('username');
-
- 原生查询
-
-
- query方法
-
$result = Db::query("SELECT * FROM user WHERE status = 1");
-
-
- execute方法
-
$rows = Db::execute("INSERT INTO user (name, age) VALUES (?, ?)", ['李四', 25]);
复习ThinkPHP框架漏洞
ThinkPHP常见漏洞
sql注入漏洞
insert
- 影响版本
-
- 5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5
- 原理:Builder 类的 parseData ⽅法中。由于程序没有对数据进⾏很好的过滤,将数据拼接进 SQL 语句,导致 SQL注⼊漏洞 的产⽣
update
- 影响版本
-
- 5.1.6<=ThinkPHP<=5.1.7 (⾮最新的 5.1.8 版本也可利⽤)
- 原理:Mysql 类的 parseArrayData ⽅法中由于程序没有对数据进⾏很好的过滤,将数据拼接进 SQL 语句,导致 SQL注⼊漏洞 的产⽣
select
- 影响版本:ThinkPHP=5.0.10
- 原理:Mysql 类的 parseWhereItem ⽅法中。由于程序没有对数据进⾏很好的过滤,直接将数据拼接进 SQL语句。再⼀个, Request 类的 filterValue ⽅法漏过滤 NOT LIKE 关键字,最终导致 SQL注漏洞的产⽣。
orderby
- 影响版本:5.1.16<=ThinkPHP5<=5.1.22
- 原理:Builder 类的 parseOrder ⽅法中。由于程序没有对数据进⾏很好的过滤,直接将数据拼接进 SQL语句,最终导致 SQL注⼊漏洞 的产⽣
聚合函数
- 影响版本:5.0.0<=ThinkPHP<=5.0.21 、 5.1.3<=ThinkPHP5<=5.1.25
- 原理:本次漏洞存在于所有 Mysql 聚合函数相关⽅法。由于程序没有对数据进⾏很好的过滤,直接将数据拼接进 SQL 语句,最终导致 SQL注⼊漏洞 的产⽣
代码执行漏洞
- 影响版本:5.0.0<=ThinkPHP5<=5.0.23 、5.1.0<=ThinkPHP<=5.1.30
-
- 原理:ThinkPHP 底层没有对控制器名进⾏很好的合法性校验,导致在未开启强制路由的情况下,⽤户可以调⽤任意类的任意⽅法,最终导致 远程代码执⾏漏洞 的产⽣
- 版本5.0.7<=ThinkPHP5<=5.0.22 、5.1.0<=ThinkPHP<=5.1.30
-
- 原理:本次漏洞存在于 ThinkPHP 底层没有对控制器名进⾏很好的合法性校验,导致在未开启强制路由的情况 下,⽤户可以调⽤任意类的任意⽅法,最终导致 远程代码执⾏漏洞 的产⽣
文件上传漏洞
- 影响版本:5.0.0<=ThinkPHP5<=5.0.18 、5.1.0<=ThinkPHP<=5.1.10
- 原理:本次漏洞存在于 ThinkPHP 模板引擎中,在加载模版解析变量时存在变量覆盖问题,⽽且程序没有对数据进⾏很好的过滤,最终导致 ⽂件包含漏洞 的产⽣
自动化扫描
搭建部署脱单交友平台并审计复现任意文件写入漏洞
环境搭建
上传并解压文件
创建网站,注意根目录是public
创建数据库,并修改配置
配置伪静态
登录测试
任意文件上传漏洞复现
上述代码中,漏洞位于 upload() 方法及其调用的辅助函数 base64Image() 中,没有对文件类型进行过滤。
将代码进行Base64编码后上传
phar反序列化漏洞原理及利用技巧
原理
- Phar反序列化是一种极其隐蔽且威力巨大的攻击方式。它打破了传统反序列化必须依赖
unserialize()函数的限制。 - 原理:Phar (PHP Archive) 是PHP的一种打包格式。其核心漏洞点在于:Phar文件的元数据(Metadata)是以序列化形式存储的。 当PHP通过
phar://伪协议解析一个Phar文件时,底层代码会自动对该文件的元数据进行反序化操作。 - 几乎所有文件操作函数(如
file_exists(),is_dir(),file_get_contents(),include等)在处理phar://开头的路径时,都会触发反序列化。 - 要在ThinkPHP环境(或其他PHP环境)中实现Phar反序列化,必须满足以下条件:
-
- 生成恶意的phar文件。
- 上传恶意的phar文件。
- 调用恶意的phar文件。
利用技巧
- 由于PHP只检查Phar文件的签名(Stub),不检查文件后缀。你可以将Phar文件伪装成JPG
- 绕过关键字phar://
compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt
php://filter/read=convert.base64-encode/resource=phar://phar.phar