2.21.5 strpos

strpos()

strpos()函数使用参考

strpos — 查找字符串首次出现的位置

int strpos( string $haystack, mixed $needle[, int $offset = 0] )

返回 needle 在 haystack 中首次出现的数字位置。 


参数 

haystack
在该字符串中进行查找。 
needle
如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符的顺序值。 
offset
如果提供了此参数,搜索会从字符串该字符数的起始位置开始统计。如果是负数,搜索会从字符串结尾指定字符数开始。 


返回值 

返回 needle 存在于 haystack 字符串起始的位置(独立于 offset)。同时注意字符串位置是从0开始,而不是从1开始的。 

如果没找到 needle,将返回 FALSE

示例代码:

<?php
$flag = "flag";

if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
echo 'You password must be alphanumeric';
else if (strpos ($_GET['password'], '--') !== FALSE)
die('Flag: ' . $flag);
else
echo 'Invalid password';
}
?>

代码审计需要满足两个条件:1. if (ereg ("^[a-zA-Z0-9]+",",_GET['password']) === FALSE) == 》 FALSE

2. if (strpos ($_GET['password'], '--') !== FALSE) == 》 TRUE 第一个进行的正则匹配是查看是否为字母或数字

利用strpos()函数不能处理数组的漏洞构造payload

构造payload:

http://123.206.87.240:9009/19.php?password[]=

实例分析

class Login {
  public function __construct($user, $pass) {
    $this->loginViaXml($user, $pass);
  }

  public function loginViaXml($user, $pass) {
    if (
      (!strpos($user, '<') || !strpos($user, '>')) &&
      (!strpos($pass, '<') || !strpos($pass, '>'))
    ) {
      $format = '<?xml version="1.0"?>' .
        '<user v="%s"/><pass v="%s"/>';
      $xml = sprintf($format, $user, $pass);
      $xmlElement = new SimpleXMLElement($xml);
      // Perform the actual login.
      $this->login($xmlElement);
    }
  }
}

new Login($_POST['username'], $_POST['password']);

我们看到 第11行 和 第12行 ,程序通过格式化字符串的方式,使用 xml 结构存储用户的登录信息。实际上这样很容易造成数据注入。然后 第21行 实例化 Login 类,并在 第16行 处调用 login 方法进行登陆操作。在进行登录操作之前,代码在 第8行 和 第9行 使用 strpos 函数来防止输入的参数含有 < 和 > 符号,猜测开发者应该是考虑到非法字符注入问题。

在上面这个例子中,strpos 函数返回查找到的子字符串的下标。如果字符串开头就是我们要搜索的目标,则返回下标 0 ;如果搜索不到,则返回 false 。在这道题目中,开发者只考虑到 strpos 函数返回 false 的情况,却忽略了匹配到的字符在首位时会返回 0 的情况,因为 false 和 0 的取反均为 true 。这样我们就可以在用户名和密码首字符注入 < 符号,从而注入xml数据。我们尝试使用以下 payload

user=<"><injected-tag%20property="&pass=

Last updated