记录一次登录绕过的代码研究
前提
首先知道框架基于ThinkPHP5.0.24
还有两个漏洞的存在
完整的SSRF
代码如下,属于未授权的方法
而且存在return
,相当于存在输出位置,可以进行文件读取
Phar反序列化
这是一个后台方法
需要最高权限3
,才能访问
漏洞代码如下,可以看到base_dir
是直接传入的,代表协议可控
后面虽然拼接了一堆堆东西,之前没有测试过,但是可以做一个测试
测试代码
测试结果
可以看到,只要phar
文件存在,后面无论加多少路径,都会触发反序列化
绕过分析
SQL注入
第一步先看继承
所有的后台控制器都继承自Base
控制器
通过Token
验证权限,而token
得值是从cookie
中获取得
这个地方如果,在没有status
得情况下,这里是不存在服务器得限制的
如果account
完全可控,在windows
下就存在完整的注入漏洞
在Linux
也也可以结合联合查询伪造数据登录
具体为什么存在注入,自行参考ThinkPHP
的not like
表达式注入
不在多解释
那为什么不存在两外一个参数的时候可以呢
因为mysql
的自带的information_schema
支持大写,且其中的表支持大小写
就导致可以通过tables
表机器逆行数据伪造,从而登陆成功
解释了这么多,还是先看看account
是否完全可控吧
跟进checkToken
方法
可以看到这里是JWT
编码
jwt_key
的值被存储在文件中,这里是hello
,也可以利用SSRF
通过file
协议读取
结论
payload
不做讨论
第二种存储XSS
限制
- 未开启
ip
检测
第三种爆破
限制
- 未开启
ip
检测
由于这是测试环境的代码,所以未开启ip
检测,但正式环境应该会开启的
在登录模块,创建token的参数中很有意思
并没有将密码的加密或者其他不可控的参数加如
导致这是一个弱加密,只需要根据一天的时间戳生成加密参数进行爆破,
只要管理员一天内登陆过
就可以登陆成功
第四种修改管理密码
限制
error_reporting(0)
- 未开启
ip
限制
在login
控制器中虽然也继承自base
,但是由于自己的初始化方法中不存在对付类初始化方法的调用,所以导致,此控制器的所有方法均可以未授权访问
在这个控制器中我们可以通过userDelete
方法禁用掉管理员的账号
但是这么做对我们并没有什么意义
所以下面还存在一个,重置密码的操作
同样我们不知道管理的密码,但是没关系
这里需要知道的是,字段名,没有大小写的限制
id
参数完全可控
这个样子我们先注册一个普通账号,假设id
为10
,管理员id
为1
通过JWT
解码后的id
如下
1 | $id= ['like',['1','10'],')or(']; |
最后通过底层代码处理后,where
语句为
1 | WHERE (`id` LIKE 1) OR (`ID` LIKE 10) AND `psd` = 'aaaaaaa' |
这个逻辑一定会查到一条数据,就是我们新增的那个账号
为什么需要关闭报错呢
这里的update
语句格式换了
1 | $setresult = Db::name("admin")->where('id', $id)->update(['psd' => $newpsd]); |
如果是如下
1 | $setresult = Db::name("admin")->where(['id'=>$id])->update(['psd' => $newpsd]); |
则完全无需关闭报错
目标大妈中的写法会触发一个list
函数的警告
就是不存在下标1
用如下代码做测试
1 | $id = $_POST['id']; |
测试结果如下
但是谁会关闭报错呢,之前还真遇到过一个
在关闭报错的情况下,最终的update
语句的where
条件是
1 | WHERE (`id` LIKE 1) OR (`ID` LIKE 10) |
这个会把id
等与1
和10
的数据都修改了,导致密码被重置
第五种新增管理员
同样在login
控制器下面存在一个新增用户的方法,但是这个方法不让添加管理员
这里刚开始考虑到了两种情况
- 数据库中的
role
字段的类型为字符型时 - 为
int
型时
字符型
字符型时,需要绕过的时权限的判断,这里对于鉴权用到的是in_array
有趣
我们知道,当后面的数组中为int
类型是,由于存在弱类型的比较,导致很简单的就绕过
那数组中为字符型时,这个特性还可以用吗
测试代码
1 |
|
测试结果
发现无法适用于适用与字符型
fuzz
一下什么字符可以绕过这个in_array
测试代码
1 | $id = $_POST['id']; |
发现.
可以的
但是不能用,可以绕过in_array
的同样可以绕过==
这个时候想起来全角字符
可以插入
但是还是全角的,所以取出来还是匹配不到没有意义
结论
字符型,比较难搞
整形
整形比较容易一些,利用点比较多一点
第一种
浮点型,在数据库中,向数据库的int
字段插入浮点,会自动强转
第二种
科学计数法
但是存在缺陷,绕不过去php
的弱类型
第三种
PDO
的参数绑定
在绑定int类型的字段时,pdo
会自动强转
这个比较简单,不举例子了
结论
如果数据库时int
类型我们可以利用此方法成果添加管理员
就知道没这么简单
然后查看了一下数据库的字段类型。
!!!!
enum类型
这个类型之前没遇到过
测试全角
测试浮点
测试pdo
测试完发现,PDO
对这个enum
类型的字段,并不会像int类型那样处理,而是原模原样的传进了数据库
最后也是报一个类型错误
模糊测试
只能按照原模原样区fuzz
一些字符了
控制器方法中的接受的参数,相当于直接使用助手函数input
测试代码如下
1 | $role = input('id');//$_POST['id']; |
心动的来了,空格成果绕过,并且,成果插入数据
数据库中已插入并且role
是3
这也算是mysql
在处理enum
类型的时候的一个小问题吧