PHP代码审计入门篇bluecms
2019-12-19 10:50:25 Author: xz.aliyun.com(查看原文) 阅读量:173 收藏

都说bluecms是代码审计入门的香饽饽,我这个web菜鸡也来凑个热闹

sql注入

安装上打开目录,随便点了一个ad_js.php
里面有个sql语句

$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);

看一下getone

function getone($sql, $type=MYSQL_ASSOC){
        $query = $this->query($sql,$this->linkid);
        $row = mysql_fetch_array($query, $type);
        return $row;
    }

是一个执行sql语句的函数
逆向追踪$ad_id变量

$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';

$ad_id是从$_GET['ad_id']中拿来的,只经过了一步trim,很明显的sql注入

试了一下发现转义了,回头看了一下,上面包含了一个common.inc.php文件
里面有一个转义的操作

if(!get_magic_quotes_gpc())
{
    $_POST = deep_addslashes($_POST);
    $_GET = deep_addslashes($_GET);
    $_COOKIES = deep_addslashes($_COOKIES);
    $_REQUEST = deep_addslashes($_REQUEST);
}

这里过滤缺少了对$SESSION的过滤,可能存在对session的注入,比如请求头之类的,或者 session中的数据
deep_addslashes函数

function deep_addslashes($str)
{
    if(is_array($str))
    {
        foreach($str as $key=>$val)
        {
            $str[$key] = deep_addslashes($val);
        }
    }
    else
    {
        $str = addslashes($str);
    }
    return $str;
}

addlashes会对这些转义
单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)

难道到这里就凉了吗,回头看了一下sql语句,发现参数位置并没有被单引号包住,那addslashes不就没用了吗


union联合查询注一下。sql注入一个

宽字节注入

还有一个问题 响应头哪里显示的content-type是 gb2312编码 可能存在宽字节注入
再去找几个登陆点试一下 有没有宽字节
在search.php中找到了一处搜索,

if(!empty($keywords))
{
    $condition .= " AND title LIKE '%".$keywords."%' OR keywords LIKE '%".$keywords."%' ";
}

$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('post')." WHERE 1=1 ".$condition);

回溯keywords 可控

$keywords = !empty($_REQUEST['keywords']) ? trim($_REQUEST['keywords']) : '';

试了一下 因为搜索型的注入要闭合一个% 所以宽字节不能用
再找找其他地方有没有数据库查询操作

首页那里有一个登录框,看看这个后台的代码

user.php 873行 这里调用了一个查询

$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$user_name'");

溯源$user_name变量

$user_name = !empty($_REQUEST['user_name']) ? trim($_REQUEST['user_name']) : '';

直接赋值 不需要绕过

测试

输入单引号 不报错
输入%df' 报错
实锤了 宽字节注入

这里不显示 查询结果的信息 可以用盲注或者报错注入

还有其他几处宽字节注入的地方
user.php的154行处 注册时 也调用了一步查询,并且$username可控

if($db->getone("SELECT * FROM ".table('user')." WHERE user_name='$user_name'")){
        showmsg('该用户名已存在');
    }

。。。
还有很多 不过原理都一样,就不多说了

在category.php 调用了url_rewrite函数,跟进一下,
url_rewrite()
涉及到一大堆的url赋值操作,但是都需要满足一定的值才可以,一开始我是这样想的 这里让他一个也不满足,利用变量覆盖,覆盖url的值,直接到return url

function url_rewrite($act, $arr)
{
    global $_CFG;
    $url = $id = $cid = $aid = $page_id = $ann_id = '';
    extract($arr);
     if($act == 'category')
    {
    .......
    .......

    return $url;
}

并且这里的if判断$act 还是一个弱类型的判断,存在一定问题

回溯$act $arr变量来源
$act变量是固定的不可控,$arr是从数据库里拿出来的
这条路貌似行不通了,因为$act不可控,不能直接覆盖url,然后返回了

接着往下看

这里有个htmlspecialchars($_POST['comment'])

$content = !empty($_POST['comment']) ? htmlspecialchars($_POST['comment']) : '';

看下htmlspecialchars函数的作用

& (& 符号)    &
" (双引号) ",除非设置了 ENT_NOQUOTES
' (单引号) 设置了 ENT_QUOTES 后, ' (如果是 ENT_HTML401) ,或者 ' (如果是 ENT_XML1、 ENT_XHTML 或 ENT_HTML5)。
< (小于)  &lt;
> (大于)  &gt;

没有开启ENT_QUOTES选项 也就是说单引号没有转义,可能存在xss
comment.php 和 guest_book.php 这两个文件一个是评论 一个是留言,大多都容易出现xss

insert型注入

guest_book.php
这里找到了一个插入的地方,本来想找xss来着 没想到找到一个sql注入
这里有个online_ip

$sql = "INSERT INTO " . table('guest_book') . " (id, rid, user_id, add_time, ip, content) 
            VALUES ('', '$rid', '$user_id', '$timestamp', '$online_ip', '$content')";
    $db->query($sql);
    showmsg('恭喜您留言成功', 'guest_book.php?page_id='.$_POST['page_id']);

获取ip很有可能是通过$SESSION来获取的,溯源$online_ip
在这个文件内,没找到$online_ip 那他的$online_ip是咋来的,回头想了一下,前面还包含了几个文件,应该是在这几个文件内,这个变量总不能是凭空来的吧
在comment.inc.php中找到

跟踪getip()

function getip()
{
    if (getenv('HTTP_CLIENT_IP'))
    {
        $ip = getenv('HTTP_CLIENT_IP'); 
    }
    elseif (getenv('HTTP_X_FORWARDED_FOR')) 
    { //获取客户端用代理服务器访问时的真实ip 地址
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    }

x-forwarded-for 很常见的ip绕过 注入一下试试

插入成功
注入
这里要注意$SESSION中的数据貌似没有自动的url解码 +不能被解码为空格

在conment.php中也存在同样的问题 113行调用了getip 直接当到了数据库里面

$sql = "INSERT INTO ".table('comment')." (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) 
            VALUES ('', '$id', '$user_id', '$type', '$mood', '$content', '$timestamp', '".getip()."', '$is_check')";

不演示了,原理都一样的

小总结一下

做到这里就能够发现,这里的与sql相关的操作,貌似都可以注入,因为过滤程序都是相同的,变量的获取方式也是相同
sql注入的漏洞到这里先放一下 太多了

任意url跳转

user.php里的代码太多了 先大体上看了一下内容,都是些if-elseif分支结构,通过判断$act来决定执行那部分分支语句

user.php 上面有几个变量 不像之前的那些文件那样经过Intval, 这两个变量都可控

$act = !empty($_REQUEST['act']) ? trim($_REQUEST['act']) : 'default';
 $from = !empty($_REQUEST['from']) ? $_REQUEST['from'] : '';

$act变量主要是作为if判断来使用的 没啥大用处,
主要注意一下$from,调用$from的地方, 当时这里应该直接搜一下的 正向的变量追踪
在112行看到 url跳转

showmsg('欢迎您 '.$user_name.' 回来,现在将转到...', $from);

在回溯一下,看看$from中间经过了那些变化
在66行

$from = !empty($from) ? base64_decode($from) : 'user.php';

很好绕过base64编码一下就行


而且后面几个地方出现了相同的问题
注册处

showmsg('恭喜您注册成功,现在将转向...', $from);

不多说了 原理和上面的一样

SSRF

还是user.php 在780行

if (strpos($_POST['face_pic1'], 'http://') != false && strpos($_POST['face_pic1'], 'https://') != false){
           showmsg('只支持本站相对路径地址');
         }

这里有一处if判断,对于strpos函数应该使用===来判断 因为0和false弱类型相等 这里过滤有问题

搜一下看哪里有调用$_POST['face_pic1']的地方

$face_pic = trim($_POST['face_pic1']);

    if(isset($_FILES['face_pic2']['error']) && $_FILES['face_pic2']['error'] == 0){
        $face_pic = $image->img_upload($_FILES['face_pic2'],'face_pic');
    }
    $face_pic = empty($face_pic) ? '' : $face_pic;
    后面就会存到数据库里面
        $sql = "UPDATE ".table('user')." SET birthday = '$birthday', sex = '$sex', face_pic = '$face_pic', email = '$email', msn = '$msn', qq = '$qq'," .
            " mobile_phone = '$mobile_phone', office_phone = '$office_phone', home_phone = '$home_phone', address='$address' WHERE user_id = ".intval($_SESSION['user_id']);

这个face_pic是通过路径引用头像的地方 很明显存在SSRF漏洞
绕过也很好绕过 前面的不上传图片,令地址http://或者https://开头就行

XSS

再看看其他的分支结构是干嘛的
在user.php的266行 看到了一个过滤

$content = !empty($_POST['content']) ? filter_data($_POST['content']) : '';

跟进一下 看看他过滤的严不严格 说不定我就是漏网之鱼

function filter_data($str)
{
    $str = preg_replace("/<(\/?)(script|i?frame|meta|link)(\s*)[^<]*>/", "", $str);
    return $str;
}

就过滤了这么几个标签,img就可以绕过
<img src onerror=alert(1)>

测试一下


弹窗

总结

这次的cms属于入门级别,漏洞比较多,一共找出来sql注入 宽字节注入 insert注入 SSRF 任意url跳转 xss这6个漏洞,可以说这些漏洞主要都是过滤不严格导致的
我的审计思路 主要是先找到危险函数,然后回溯可控变量
中间也踩到了不少的坑,危险函数挺多的, 但是回溯一下可控变量,就没找到几个可以利用的


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