php夺旗题探究

夺旗题在信息安全领域一般被称为CTF题,随着这几年各方对信息安全慢慢变得越来越重视,CTF也渐渐火了起来。今天就记录一下跟这个有关的东西。

场景: 下班前,看到同事在看一个CTF题。

代码如下

<?php
header('content-type:text/html;charset=utf-8');
require_once '../config.php';
function decode($data){
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
    mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
    $data = mdecrypt_generic($td,base64_decode(base64_decode($data)));
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    if(substr(trim($data),-6)!=='_mozhe'){
        echo '<script>window.location.href="/index.php";</script>';
    }else{
        return substr(trim($data),0,strlen(trim($data))-6);
    }
}
$id=decode($_GET['id']);
$sql="select id,title,content,time from notice where id=$id";
$info=$link->query($sql);
$arr=$info->fetch_assoc();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>X公司HR系统V1.0</title>
<style>.body{width:600px;height:500px;margin:0 auto}.title{color:red;height:60px;line-height:60px;font-size:30px;font-weight:700;margin-top:75pt;border-bottom:2px solid red;text-align:center}.content,.title{margin:0 auto;width:600px;display:block}.content{height:30px;line-height:30px;font-size:18px;margin-top:40px;text-align:left;color:#828282}</style>
</head>
<body>
<div class="body">
<div class="title"><?php echo $arr['title']?></div>
<div class="content"><?php echo $arr['content']?></div>
</body>
</html>

拿到代码,就知道了CTF的第一步。 要想做这类的题目,读不了代码肯定是不行的。 所以我们知道了,做这类题目的首要条件就是得有一定的代码基础。

这段代码的意思是,封装了一个decode方法并且给了一个形参,在方法体内对形参进行处理,然后回到代码执行流程中,在给id变量赋值的时候,去应用这个方法,然后就把变量带入了sql query中。这里不是直接query的,通过link对象去访问的query方法,这个时候,获取的数据赋给info变量后,实际上还是一个object,然后再通过对象访问方式获取一组数据赋给变量arr。 这里,最终查出来的数据其实是这样的。

arr[

‘id’=>’0’;
‘title’=>’零’;
‘content’=>’我是0零零’;
‘time’=>’我可能是一个unix时间戳’;
]

所以,$arr[‘title’] 下标对应的是谁得到的自然就是谁的value。简单点理解就是,从数据库中查出来的是一些键->值对应的数据,查出来了之后又赋值给了变量arr, 我通过变量[‘键’],取出来的自然就是值了。

到这里,我们知道第二条件, 要想做这类的题目, 不仅会读代码,还需要了解代码中具体方法的含义。

关键点其实在这里

if(substr(trim($data),-6)!=='_mozhe'){
        echo '<script>window.location.href="/index.php";</script>';
    }else{
        return substr(trim($data),0,strlen(trim($data))-6);
    }

当只有满足else条件时, 才会return结果。 可以连贯起来看一下。 部分翻译成中文

我是一个值,不知道为什么,我总想找一个叫id的东西去跟它结婚,但id是不认识我的,单纯的硬碰硬,它根本就不认识我是谁啊?无奈,叹息了半天突然想到了解决办法,去买辆车啊,于是,伴随着一阵突突突突的声音,手扶拖拉机开回了家。有了车($_GET),它终于答应肯坐上车等夫婿了($_GET[‘id’]),于是我便开始准备向它灌输自己,这样以后别人获取id的夫婿时,自然就知道那就是我了。(嘿嘿嘿,想想还有点小激动呢)正在灌输时,发现灌输后获取不到任何应答(未得到query结果),最后猛然发现…id一直不理解我的原因是因为中间杀出了丈母娘(decode),我们的中间谈话被丈母娘窃听后进行了解密(实际上是加密,因为我要传加密数据才会被识别)再传送,这样无论如何,我告诉它任何明文,它都无法理解了。。相当于是这样的:

我:i miss you, can you help me?

它 recv :$%sghs*(&^*()??

我: good day, you are beautiful!

它 recv: (*&#@#$???!#_&^%%??

那既然丈母娘对通话进行了(解密)加密, 没办法,要想抱得id归,那只有去看看丈母娘到底怎么样才肯让我得到id的回应了。 (else里的return结果) 我们通过丈母娘,找到了它对通话进行加密的方法实现部分(function decode{}中的所有代码段)发现是这样的。

我不管对id说什么,都被识别成了$data, 进入到加密方法体内部后,发现它首先是进行了一个加密实现,通过mcrypt_module_open方法打开一个加密方法和模式,它可接受的默认有四个参数。

resource mcrypt_module_open ( string 算法名 , string 加密模块的目录,可以留空 , string $加密模式, string $加密模式的位置,也可以留空)

所以这里我们知道了, 丈母娘对通话进行加密的算法对应模块名叫MCRYPT_RIJNDAEL_128(AES)加密模式是CBC。继续往下看,是使用mcrypt_generic_init方法对加密进行初始化,它可接受的参数有3个,分别为,加密描述符(就是老铁你用的什么加密啊? 加密模式是啥啊?),第二个是密钥的最大长度,第三个是密钥的分组大小(CBC是一块一块的),再往下,调用mdecrypt_generic进行解密。它的参数就是加密描述(用的什么加密,模式是什么,最大长度分组大小之类的。)和密文,这里的就是 base64_decode(base64_decode($data)), 然后再使用mcrypt_generic_deinit终止加密使用mcrypt_module_close关闭加密,trim是去空白,字符串中的空白都会被去除,如果从最后一位开始算起截取6位不等于_mozhe,将会进入首页,所以这里加密前缀不管是多少位,后6位必须都得是_mozhe,比如asdkxcs_mozhe这种,最后返回数据时,因为取的是-6-0的结果,所以这里实际上是从第6位开始返回。

所以,这里可得出加密

$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,”,MCRYPT_MODE_CBC,”);
mcrypt_generic_init($td,’ydhaqPQnexoaDuW3′,’2018201920202021′);
$data = mcrypt_generic($td,base64_encode(base64_encode($data)));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

解密后的字符串后面开始算起6位等于_mozhe,就会进入数据返回条件。这里因为是sql注入,所以只进入条件还不行,比如,这个注入该如何让它进行报错? 举个例子,如果解密后的数据是 ‘_mozhe, 那自然取到的就会是一个’ sql语句会不会报错呢?

这里给一个拓展实例,道理其实是相同的

<?php
function decbase($data)
{
  $data = base64_decode($data);
  if(substr(trim($data),-7)!=='confrim')
  {
      echo 'error';
  }
  else
  {
      return $data;
  }
}

$data = decbase($_GET['data']);
var_dump($data);

当输入任意字符时, $data变量这个时候得到的值始终都会是error,即使位数满足7位,也会是error,当输入cmRzcmRzY29uZnJpbQ==时,输出了base64的结果。

此条目发表在php security分类目录,贴了, , , , , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用*标注