基于框架开发的程序的代码审计思路2
2019-08-27 10:01:00 Author: xz.aliyun.com(查看原文) 阅读量:151 收藏

0x01:

上文地址:http://www.f4ckweb.top/index.php/archives/62/
在文章前面BB两句为什么在我博客,完全是因为不知道先知的审核规则,所以发到博客了。

上一节说了基于系统框架的审计思路,这一节说一下关于开发者自写的框架的审计思路。

0x02:

在上一节中说到,MVC模式中一般只需要看cotroller中的代码,因为那里面才是能够直接访问到的。但是其实有一点忘记说了,其实也可以直接看框架的核心文件。所以对于自写框架我的审计思路一般是:核心文件->调用地点

而过程一般会先观察程序的架构(由目录命名规则就可以看出来)

0x03:

本文用到的案例为UQCMS,这是一套电商相关的程序,可自行百度下载。

0x04:

在框架的核心代码中

0x01(SQL):

首先查看目标程序的目录结构:

由此可见框架核心代码在:UQframework中,模型代码在:module中。

进入UQframework后,发现果然是目标框架。

观察目录以及文件名字可以得出文件具体功能,以db.class.php举列子,发现开发者后门:

function query($uq1) {
        if (empty($uq1)) {
            return false;
        }
        $t1 = microtime(true);
        $uq3 = $this->conn->query($uq1);
        $uq4 = microtime(true) - $t1;
        if ($uq4 >= '0.1') {
            db_log(date('Y-m-d H:i:s') . ' SLOW: ' . $uq1);
        }
        if ($this->conn->error) {
            echo $this->conn->error;
            echo '<br>';
            echo $uq1 . '<br>';
            db_log(date('Y-m-d H:i:s') . ' ERROR: ' . $uq1);
            return false;
        }
        if ($uq3) {
            return $uq3;
        } else {
            return false;
        }
    }

在query方法中,并没有任何过滤,直接就带入数据库进行查询了,而其他插入均存在过滤:

function add($uq6, $uq7) {
        $uq8 = "";
        $uq9 = "";
        foreach ($uq7 as $uq10 => $uq11) {
            if (@trim($uq11) !== '') {
                $uq8.= "`" . $uq10 . "`,";
                if (!get_magic_quotes_gpc()) {
                    $uq9.= " '" . @addslashes($uq11) . "',";
                } else {
                    $uq9.= " '" . $uq11 . "',";
                }
            }
        }
        $uq8 = rtrim($uq8, ",");
        $uq9 = rtrim($uq9, ",");
        $uq1 = "INSERT INTO `{$uq6}` (" . $uq8 . ") VALUES  (" . $uq9 . ");";
        return $this->query($uq1);
    }

查找调用query函数并且没有走过滤的函数,在此文件中有很多,但是调用最多的为get_one函数:

function get_one($uq1) {
        $uq3 = $this->query($uq1);
        if ($uq3) {
            $uq7 = $uq3->fetch_array(MYSQLI_ASSOC);
            if ($uq7) {
                return $uq7;
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

通过seay的工具,搜索出来很多前台的功能点:

/home/controls/demo.class.php

function index() {
        $uq0 = $_GET['gid'] ? ? false;
        $uq1 = $this->db->get_one("select * from " . table('goods') . " where id = " . $uq0);
        if ($uq1['id']) {
            $uq2 = '';
            if (cfg('distribute_status') == '1') {
                if (!empty($this->uid)) {
                    $uq3 = $this->db->get_one("select id,uid from " . table('distribute') . " where status = 1 and uid = " . $this->uid);
                    if ($uq3['id']) {
                        $uq2 = '?trackuid=' . $uq3['id'];
                    }
                }
            }
            $uq4 = cfg('site_url') . 'goods/' . $uq1['id'] . '.html' . $uq2;
            $uq5 = ['img' => [['url' => img_host() . $uq1['images'] . img_size('640x640') ]], 'font' => [['content' => $uq1['title'], 'x' => 20, 'y' => 710, 'width' => 320, 'height' => 80, 'size' => 18], ['content' => '现价', 'x' => 20, 'y' => 820, 'size' => '24', 'color' => '255,0,0,0'], ['content' => '¥' . $uq1['price'], 'x' => 110, 'y' => 820, 'color' => '255,0,0,0', 'size' => '27']], 'qrcode' => ['content' => $uq4, 'x' => 410, 'y' => 680, ]];
            images::init()->create($uq5);
        }
    }

而框架之所以难以挖掘漏洞,完全是因为基本上所有的框架都会有一层强到变态的waf,参考该框架的以下代码:

set_error_handler("customError", E_ERROR);
$uq4 = "'|<[^>]*?>|^\\+\/v(8|9)|\\b(and|or)\\b.+?(>|<|=|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$uq5 = "^\\+\/v(8|9)|\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$uq6 = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
function StopAttack($uq7, $uq8, $uq9) {
    if (preg_match("/" . $uq9 . "/is", $uq7) == 1) {
        safe_error_msg();
    }
    $uq8 = arr_foreach($uq8);
    if (preg_match("/" . $uq9 . "/is", $uq8) == 1) {
        safe_error_msg();
    } else {
        preg_match_all('/<\\s*img[^>]*>/Ui', $uq8, $uq10);
        if (!empty($uq10[0])) {
            foreach ($uq10[0] as $uq11) {
                if ((!strpos($uq11, 'onerror') == '') || (!strpos($uq11, 'javascript') == '')) {
                    safe_error_msg();
                }
            }
        }
    }
}

简单点的payload:

http://127.0.0.1/index.php/demo/index/?gid=2/**/and(updatexml(1,concat(0x7e,user(),0x7e),1))

这样的拦截基本上只能够在变量覆盖,IP地址,多次解码上面做一点文章。但是由于代码并没有格式化,所以楼主也没有去找这些地方

0x02(上传):

UQframework\class\upload.class.php:

static public function store($uq2, $uq3 = null, $uq4 = null) {
        if (empty($uq4)) {
            $uq5 = array();
            $uq6 = array_values($_FILES);
            $uq0 = $uq6[0]['name'];
            $uq7 = $uq6[0]['type'];
            $uq8 = $uq6[0]['size'];
            $uq9 = $uq6[0]['tmp_name'];
        } else {
            $uq0 = $uq4;
            $uq7 = mime_content_type($uq4);
            $uq8 = filesize($uq4);
            $uq9 = $uq4;
        }
        if ($uq0) {
            if (in_array($uq7, ['image/jpeg', 'image/gif', 'image/png'])) {
                if ($uq8 < ((1024 * 1024) * 10)) {
                    if (empty($uq3)) {
                        $uq3 = $uq2 . '/' . self::reset_new_name($uq0);
                    }
                    if (!_is_dir($uq3)) {
                        file::mk_dir($uq3);
                    }
                    if (file::remove($uq9, $uq3)) {
                        $uq5['error'] = '0';
                        $uq5['name'] = $uq3;
                        $uq5['url'] = cfg('site_url') . $uq3;
                        $uq5['size'] = filesize($uq3);
                        return $uq5;
                    } else {
                        return ['error' => '1', 'msg' => '上传错误'];
                    }
                } else {
                    return ['error' => '1', 'msg' => '上传文件过大,当前图片' . formatBytes($uq8) ];
                }
            } else {
                return ['error' => '1', 'msg' => '不支持此类型的文件,支持:jpg,gif,png'];
            }
        } else {
            return ['error' => '1', 'msg' => '上传的文件不能为空'];
        }
    }

可以很明显的发现上面的代码对图片的验证仅仅就只有一句代码:

if (in_array($uq7, ['image/jpeg', 'image/gif', 'image/png']))

接下来搜索store此函数:

/home/controls/album.class.php:

public function upload_temp() {
        $uq2 = upload::store('temp/images');
        echo json_encode($uq2);
    }

构造表单:

<form action="http://127.0.0.1/index.php/album/upload_temp/" method="post" enctype="multipart/form-data">
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>

0x04:

说完核心代码,我们在说说模型。什么是模型?
网上的定义为:

程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

也就是说框架的核心代码其实也可以称为模型

0x001:

在模型中也有一处文件上传,代码如下:

function upload($uq0, $uq2 = '0', $uq3 = null) {
        $uq1 = array();
        $this - >save_path = $uq0;
        $this - >ptype = $uq2;
        $this - >new_name = $uq3;
        $uq4 = array_keys($_FILES);
        $uq4 = $uq4[0];
        $uq5 = array_values($_FILES);
        $uq6 = $uq5[0]['name'];
        $uq7 = $uq5[0]['type'];
        $uq8 = $uq5[0]['size'];
        $uq9 = $uq5[0]['tmp_name'];
        $uq1 = $this - >upload_function($uq4, $uq6, $uq7, $uq8, $uq9);
        if ($uq1['error'] == 0) {
            return $uq1;
        } else {
            return $uq1;
        }
    }

跟进upload_function方法:

function upload_function($uq4, $uq6, $uq7, $uq8, $uq9) {
        if ($uq6) {
            if (in_array($uq7, ['image/jpeg', 'image/gif', 'image/png', 'jpg', 'gif', 'png'])) {
                if ($uq8 < (1024 * 1024 * 2)) {
                    if (empty($this - >new_name)) {
                        $uq3 = 'data/'.$this - >save_path.'/'.$this - >reset_name($uq6);
                    } else {
                        $uq3 = $this - >new_name;
                    }
                    if (cfg('storage_status') == '1') {
                        $uq10 = new storage();
                        $uq1 = $uq10 - >upload($uq9, $uq3);
                    } else {
                        file: :mk_dir($uq3, true);
                        if (file: :remove($uq9, $uq3)) {
                            $uq1['error'] = '0';
                            $uq1['name'] = $uq3;
                            $uq1['size'] = filesize($uq3);
                            $uq1['url'] = $uq3;
                        } else {
                            return ['error' = >'1', 'msg' = >'文件移动错误'];
                        }
                    }
                    if ($uq1['error'] == '0') {
                        $uq11 = $this - >space_size($uq1['name'], $uq1['size'], $this - >ptype);
                        if ($uq11) {
                            $uq12['error'] = '0';
                            $uq12['id'] = $uq11;
                            $uq12['name'] = $uq1['name'];
                            $uq12['url'] = $uq1['url'];
                            if (isset($uq4)) {
                                $uq12[$uq4] = $uq1['url'];
                            }
                            return $uq12;
                        } else {
                            return ['error' = >'1', 'msg' = >'录入系统错误'];
                        }
                    } else {
                        return ['error' = >'1', 'msg' = >$uq1['msg']];
                    }
                } else {
                    return ['error' = >'1', 'msg' = >'上传文件过大,当前图片'.formatBytes($uq8)];
                }
            } else {
                return ['error' = >'1', 'msg' = >'不支持此类型的文件,支持:jpg,gif,png'];
            }
        } else {
            return ['error' = >'1', 'msg' = >'上传的文件不能为空'];
        }
    }

payload和上文中一样。

总结:

在审计自写程序时,可以重点关注核心文件以及模型方法,然后在查看哪些地方调用了该方法/函数

另外各位可以下载UQCMS练手,这套CMS是真的很简单


文章来源: http://xz.aliyun.com/t/6075
如有侵权请联系:admin#unsafe.sh