# 2.21.4 md5

## md5()

md5()函数使用参考

```php
md5 — 计算字符串的 MD5 散列值

string md5( string $str[, bool $raw_output = false] )


参数 

str
原始字符串。 
raw_output
如果可选的 raw_output 被设置为 TRUE，那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。 


返回值 

以 32 字符十六进制数字形式返回散列值。
```

示例代码：

```php
<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>
```

代码审计需要满足两个条件：1. username和password的值不能相同

2\. username和password的MD5值相同 利用MD5函数不能处理数组进行构造payload

> <http://123.206.87.240:9009/18.php?username[]=6&password[]=2>

## Raw MD5 Hash引发的注入

根据描述

如果可选的 raw\_output 被设置为 TRUE，那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。

先来看下效果：

![](http://ww1.sinaimg.cn/large/0065jC22ly1g171bfy6ctj30kq06e3yj.jpg) ![](http://ww1.sinaimg.cn/large/0065jC22ly1g171bx8ihoj30ld07zwef.jpg)

```php
class LoginManager {
  private $em;
  private $user;
  private $password;

  public function __construct($user, $password) {
    $this->em = DoctrineManager::getEntityManager();
    $this->user = $user;
    $this->password = $password;
  }

  public function isValid() {
    $user = $this->sanitizeInput($this->user);
    $pass = $this->sanitizeInput($this->password);

    $queryBuilder = $this->em->createQueryBuilder()
      ->select("COUNT(p)")
      ->from("User", "u")
      ->where("user = '$user' AND password = '$pass'");
    $query = $queryBuilder->getQuery();
    return boolval($query->getSingleScalarResult());
  }

  public function sanitizeInput($input, $length = 20) {
    $input = addslashes($input);
    if (strlen($input) > $length) {
      $input = substr($input, 0, $length);
    }
    return $input;
  }
}

$auth = new LoginManager($_POST['user'], $_POST['passwd']);
if (!$auth->isValid()) {
  exit;
}
```

从 第17行-20行 代码中明显存在SQL语句拼接的形式，而 $pass 变量和 $user 变量是在 第30-31行 中通过 POST 方式由用户进行控制。这里很明显存在SQL注入漏洞

程序代码 第14行 调用 sanitizeInput 函数针对用户输入的 $user 变量进行了处理，跟进一下 sanitizeInput 函数，在 第25行 找到这个函数，这个函数的作用就是调用 addslashes 函数针对输入数据进行处理。

所以按照这种情况下这个地方，似乎不存在注入点了，先别急，我们继续往下看，我们看到 第13行 代码针对用户输入 password 的值调用 md5 函数进行相关处理

我们知道我可以控制的点有两个变量，一个是 $user ，一个是 $pass ，$pass 经过了 md5 的处理，但是返回字段不是标准的md5值，$user 经过了 addslashes 函数的处理，无法引入特殊符号去闭合。这里做个假设，如果我们经过 $pass = md5($this->password, true); 处理之后的值逃逸出一个反斜杆，那么实际上带入到数据库的值就如下所示：

> select count(p) from user s where password='xxxxxx\\' and user='xxx#'

如果这种情况发生，实际上也存在了SQL注入。我们尝试fuzz一下，看看会不会存在某个值经过了 md5(xxx, true) 处理之后，最后一位是反斜杠。

我们针对1-1000进行一下fuzz，发现 md5(128, true) 最后的结果带有反斜杠。因此这题最后的payload如下：

> user= OR 1=1#\&passwd=128

带入到数据库查询的语句如下：

> select count(p) from user s where password='v�an���l���q��\\' and user=' OR 1=1#'

## 实例

```php
$password=$_POST['password'];
    $sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
    $result=mysqli_query($link,$sql);
        if(mysqli_num_rows($result)>0){
            echo 'flag is :'.$flag;
        }
        else{
            echo '密码错误!';
        }
```

从上图中的代码中的 第5行 可以看到，当查询结果返回大于0的时候，就会输出 flag ，我们前面分析过当 md5 函数的 $raw\_output 设置会true的时候， md5 函数返回前16字节长度的原始二进制，然后再将二进制转换成字符串，这种情况下可能会引入单引号等特殊字符。

有人尝试过破解这个类型的字符，目前已知两个是 ffifdyop 和129581926211651571912466741651878684928 ，我们来看看实际效果。

```
原先：SELECT * FROM admin WHERE username = 'admin' and password = 'md5($password,true)'
变成：SELECT * FROM admin WHERE username = 'admin' and password = ''or'6\xc9]\x99'
```

![](http://ww1.sinaimg.cn/large/0065jC22ly1g171rc1qhxj30mj06a74g.jpg)

由于 and 运算符优先级比 or 高，所以前面的：id=1000 and password = '' 会先执行，然后将执行结果与后面的 '6\xc9]\x99' 进行 or 运算。在布尔运算中，除了 0、'0'、false、null ，其余结果都为真。所以整个 SQL 语句的 where 条件判断部分为真，这样可定就能查出数据。

## md5 碰撞相关问题

代码如下：

```php
<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "flag{*}";
} else {
echo "false!!!";
}}
else{echo "please input a";}
?>
```

思路很明确，a的值不能等于QNKCDZO但是a经过md5后的值必须与QNKCDZO的值相等

因为php 0e开头的字符串都是==的，不是===

如：

> var\_dump("0e462097431906854"=="0e83040041");

上面这个返回true，如果题目的md5是0e开头的，

如

```
md5('240610708') 的结果是：0e462097431906509019562988736854 
md5('QNKCDZO') 的结果是：0e830400451993494058024219903391
```

还有240610708、QNKCDZO、aabg7XSs、aabC9RqS

所以我们传递任意一个进去即可(当为===时不可以)

\===时可以参考

> <https://xz.aliyun.com/t/2232>

## 修复建议

建议在使用 md5 函数的时候，不要将 $raw\_output 字段设置为true 。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ninjia.gitbook.io/secskill/serve/php/md5.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
