PHP学习笔记

PHP代码审计简单思路

2020-11-05  本文已影响0人  赵客缦胡缨v吴钩霜雪明

PHP常用框架

Zendframwork、Yii、Laravel 、ThinkPHP。。。

这里举例因为thinkphp由国内人开发用户量较多而且历史漏洞也多

Thinkphp历史漏洞很多,对于漏洞形成原因可以自己复现。

篇幅有限只介绍披露漏洞

Update方法 低于3.2.3 有sql注入问题

/**
     * 更新记录
     * @access public
     * @param mixed $data 数据
     * @param array $options 表达式
     * @return false | integer
     */
    public function update($data,$options) {
        $this->model  =   $options['model'];
        $this->parseBind(!empty($options['bind'])?$options['bind']:array());
        $table  =   $this->parseTable($options['table']);
        $sql   = 'UPDATE ' . $table . $this->parseSet($data);
        if(strpos($table,',')){// 多表更新支持JOIN操作
            $sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');
        }
        $sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');
        if(!strpos($table,',')){
            //  单表更新支持order和lmit
            $sql   .=  $this->parseOrder(!empty($options['order'])?$options['order']:'')
                .$this->parseLimit(!empty($options['limit'])?$options['limit']:'');
        }
        $sql .=   $this->parseComment(!empty($options['comment'])?$options['comment']:'');
        return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
    }

在github上也有历史分支可以查看修复代码.

业务逻辑

想要对整体的逻辑进行审计

1.  熟悉业务场景

2.  熟悉业务流程

3.  通读代码

多线程引发的漏洞

<?php
$money=100;
//数据库查询的用户余额
$buy=intval($_GET['buy']);
if ($money>0 && $money-$buy>0){
    sleep(10);
    $moeny -= $buy;
    //写入数据库
}
return $money

正常情况下用户余额一定不为负数 如果在并发情况下呢?

用户发送恶意并发请求时就有可能出现这种情况。这么防御呢

这里需要知道事务和锁的概念可以自行百度理解我这里简单概述一下

事务:类似一个执行任务 成功就任务完成 ,失败任务自动回滚到未接任务前

锁:悲观锁,乐观锁。

我们可以把多线程请求变成单线程处理,这里也可以用队列压入压出。

<?php
$money = 100;//数据库查询的用户余额
$buy = intval($_GET['buy']);
try {
    if (flock($money, LOCK_EX)) {
        if ($money > 0 && $money - $buy > 0) {
            sleep(10);
            $moeny -= $buy;
            //写入数据库A
            throw new ExceptionNew("xp");
            //写入数据库B
        }
        flock($money, LOCK_UN);
    }
} catch (Exception $exceptione) {
    throw new ExceptionNew("xp");
}
return $money

这样确实解决了这个并发问题,但又有另外一个问题,如果有多个数据库操作中间一段中断是无法对数据还原的,这里我们需要把事务也加上同时默认加锁。

我们修改一下代码看一下

<?php
$money=100;//数据库查询的用户余额
$buy=intval($_GET['buy']);
try{
    $this->startTrans();//开启事务
    if ($money>0&& $money-$buy>0){
        sleep(10);
        $moeny-=$buy;
        $this->commit(); //提交事务
        //写入数据库
    }
}catch (Exception $exceptione){
    $this->rollback();//回滚
}
return $money;

<?php
$buy=intval($_GET['buy']);
try{
    $this->startTrans();//开启事务
    $money=100;//数据库查询的用户余额
    if ($money>0&& $money-$buy>0){
        sleep(10);
        $moeny-=$buy;
        $this->commit(); //提交事务
        //写入数据库
    }
}catch (Exception $exceptione){
    $this->rollback();//回滚
}
return $money;

在加了事务的悲观锁后,所有请求到已经开启事务的代码,都会进行阻塞只有提交了事务或者回滚才会处理下一个请求。

然而这样的代码并不能防御并发。这也是很多开发中的问题,确实做了事务加锁,依然没有用。加事务必须是在查询内加,不然依旧会造成并发问题。我们在改改把读放入事务锁中。

<?php
$buy=intval($_GET['buy']);
try{
    $this->startTrans();//开启事务
    $money=100;//数据库查询的用户余额
    if ($money>0&& $money-$buy>0){
        sleep(10);
        $moeny-=$buy;
        $this->commit(); //提交事务
        //写入数据库
    }
}catch (Exception $exceptione){
    $this->rollback();//回滚
}
return $money;

这样也解决了脏读的问题。

脏读:(针对未提交数据)如果一个事务中对数据进行了更新

但事务还没有提交**,另一个事务可以“看到”该事务没有提交的更新结果,

这样造成的问题就是,如果第一个事务回滚,

那么,第二个事务在此之前所“看到”的数据就是一笔脏数据。

当然也有更复杂的情况可能框架有多个端。这种二次利用的情况更加难以审计。

在实际审计中我们想要精通一个语言的代码审计我们要做的更难

1.  要比产品更懂业务

2.  要比测试更懂流程

3.  要比开发更懂代码

4.  要比架构更懂框架
上一篇 下一篇

猜你喜欢

热点阅读