前言

根据红日安全写的文章,学习PHP代码审计的第四节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完题目会用一个实例来加深巩固,这是之前写的,有兴趣可以去看看:

PHP代码审计01之in_array()函数缺陷

PHP代码审计02之filter_var()函数缺陷

PHP代码审计03之实例化任意对象漏洞

漏洞分析

现在咱们看第一题,代码如下:

<?php
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']);
?>

题目分析

我们来看上面的代码,看第1214行,这里通过格式话字符串的方式,使用XML结构来存储用户的登陆信息,这样很容易造成注入,再看上面的代码,最后一行实例化了这个类,17行调用了login来进行登陆操作。下面到重点了,看代码810行,这里用到了strpos()函数来进行过滤<>符号,这个函数的用法如下:





如下:

<?php
var_dump(strpos("abcd","a"));
var_dump(strpos("abcd","y"));
?>



我们发现,找到子字符串的话就会返回对应的下标,没找到会返回false。而在PHP中,0和false的取反都是true,这点需要我们注意,这道题目就是开发者在使用这个函数时,只考虑了返回false的情况,而没有考虑当首字符匹配时返回0的情况。导致了过滤被绕过从而实施XML攻击。

现在知道了如何绕过,那么构造payload:user=<"><injected-tag%20property="&pass=<injected-tag>

为了更好的理解,来看一下构造这个payload时,strpos()函数的返回结果如下:

$user='<"><injected-tag property="';
$pass='<injected-tag>';
var_dump(strpos($user,'<'));
var_dump(!strpos($user,'<'));
var_dump(strpos($user,'>'));
var_dump(!strpos($user,'>'));



是不是理解了一些呢,现在看上面的代码,思考一下如果我们使用这个payload会返回什么呢。

var_dump((!strpos($user, '<') || !strpos($user, '>')) &&
(!strpos($pass, '<') || !strpos($pass, '>')));



返回了true,其实就是var_dump((true || false) &&(true || false))

成功绕过,可以进行XML注入。

通过上面的学习讲解是不是对strpos()函数了解更深一些了呢?下面咱们看一个实例,这个实例也是设计者没有考虑周全,导致任意用户密码重置。

实例分析

这次的实例是DeDecms V5.7SP2正式版,源码可以从网上下载搭建,这个很容易找到,就不详细说了,上面说了,这个漏洞是任意用户密码重置,下面我们来分析代码,漏洞的触发点在 member/resetpassword.php 文件中,是因为对接收的参数safeanswer没有进行严格的类型判断导致被绕过。下面看代码:



现在来对上面的代码做一个分析,当$dopost==safequestion时,通过$mid对应的id值来查询当前用户的safequestion,safeanswer,userid,email等值,现在来看第84行,这里的意思是当我们传入的安全问题和安全答案等于之前设置的值时,就传入sn()函数,重点来了,注意看,这里用的是双等于来验证,而没有用三等于,所以,这里是可以被绕过的。当用户没有设置安全问题时,那么默认情况安全问题值为0,安全答案值为null,这里指的是数据库中的值,而我们如果传入空值时,那么就是空字符串,84行语句也就变成了if('0' == '' && null == ''),也就是if(false&&true),所以我们只需要让前半部分转为true就可以了,通过测试如下图,都可以和0比较等于true。

if("0"=="0.0"){echo "成功1"."\n";}
if("0"=="0e1"){echo "成功2"."\n";}
if("0"=="0e12"){echo "成功3"."\n";}
if("0"=="0e123"){echo "成功4"."\n";}
if("0"=="0."){echo "成功5"."\n";}

上面的几种payload均能使得 ​safequestion 为 true,成功进入sn()函数



我们跟进sn()函数,代码如下:



在sn()内部,会根据id到pwd_tmp表中判断是否存在对应的临时密码记录,根据结果确定分支,走向 newmail 函数。现在我们假设第一次来进行忘记密码操作,那么现在的$row的值应该为空,也就会进入if(!is_array($row))分支,然后在newmail()函数中执行INSERT操作,具体代码在这个文件的上面,如图:



这个代码的功能是发送邮件到相关邮箱,并插入一条记录到dede_pwd_tmp表中,漏洞触发点在这里,我们现在看92~95行。如果 ($send == 'N') 这个条件为真,那就跳转到修改页,通过 ShowMsg 打印出修改密码功能的链接。拼接的url为:

http://www.dmsj.com/DedeCMS-V5.7-UTF8-SP2/uploads/member/resetpassword.php?dopost=getpasswd&id=$mid&key=$randval

现在咱们跟进dopost=getpasswd去看看,在member/resetpassword.php文件中,代码如下:

这里用empty()函数来判断$id和$row是否为空,如果不为空的话,就继续向下走,进入if(empty($setp))中,先判断是否超时,如果没超时的话进入修改页面,我圈起来了,现在跟过去看一下具体代码如下:



发现数据包中 $setp=2,所以代码功能又回到了member/resetpassword.php文件中,如下:



分析上面代码,我们发现如果传入的$key和数据库中的$row['pwd']相同时,则完成密码的重置,也完成了攻击的分析过程。

漏洞验证

现在注册两个账号,分别是test123和test456,查看mid的值如下,test456的mid为3。test123的mid为2。



我现在登陆test456账户,访问咱们构造的payload。来修改test123的密码。

http://www.dmsj.com/DedeCMS-V5.7-UTF8-SP2/uploads/member/resetpassword.php?dopost=safequestion&safequestion=0e1&safeanswer=&id=2

我现在登陆的是test456账户,访问url抓包。



获取到了key值,然后来访问修改密码的url:

http://www.dmsj.com/DedeCMS-V5.7-UTF8-SP2/uploads/member/resetpassword.php?dopost=getpasswd&id=2&key=xy8UzeOI



我们发现到了修改密码的页面,直接可以修改密码。我们将密码修改为abcdef,然后登陆test123,发现登陆成功,密码成功被修改。

小结

通过这篇文章的学习与讲解,是不是对strpos()函数和PHP弱类型绕过有了一定的了解了呢?下一篇文章会对escapeshellarg与escapeshellcmd使用不当来进行学习与讲解,一起努力吧!

PHP代码审计04之strpos函数使用不当的更多相关文章

  1. ***用php的strpos() 函数判断字符串中是否包含某字符串的方法

    判断某字符串中是否包含某字符串的方法 if(strpos('www.idc-gz.com','idc-gz') !== false){ echo '包含'; }else{ echo '不包含'; } ...

  2. PHP strpos() 函数

    定义和用法 strpos() 函数返回字符串在另一个字符串中第一次出现的位置. 如果没有找到该字符串,则返回 false. 语法 strpos(string,find,start) 参数 描述 str ...

  3. 字符串处理——strpos()函数

    strpos() 函数返回字符串在另一个字符串中第一次出现的位置. 大小写敏感 如果没有找到该字符串,则返回 false. strpos(string,find,start)  string 必需:规 ...

  4. PHP strlen()函数和strpos()函数

    strlen()  函数返回字符串的长度(字符数) 代码:   <?php echo strlen("Hello world!"); ?> 上面的代码将输出:12   ...

  5. php中strpos()函数

    1,strpos()函数 mixed strops(]) 返回needle在haystack中首次出现的数字位置,从0开始查找,区分大小写. 参数:haystack,在该字符串中进行查找. needl ...

  6. php strpos() 函数介绍与使用方法详解

    本文主要和大家介绍PHP中mb_strpos的使用技巧,通过使用语法以及实例给大家详细分析了用法,需要的朋友参考学习下.希望能帮助到大家.mb_strpos(PHP 4 >= 4.0.6, PH ...

  7. php strpos()函数 语法

    php strpos()函数 语法 作用:寻找字符串中某字符最先出现的位置.大理石平台怎么选择 语法:strpos(string,find,start) 参数: 参数 描述 string     必需 ...

  8. PHP代码审计02之filter_var()函数缺陷

    前言 根据红日安全写的文章,学习PHP代码审计审计的第二节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完这个题目,会有一道CTF题目来进行巩固,外加一个实例来深入分析,想了 ...

  9. php基础04:字符串函数

    <?php //1.strlen(),strlen() 函数返回字符串的长度,以字符计. echo strlen("hello world"); echo "< ...

随机推荐

  1. vue学习02-v-text

    vue学习02-v-text 引入环境版本,cdn网络引用或者本地js应用 html的结构,一般是div 创建vue实例 el:挂载点 v-text指令的作用是设置标签的内容 默认写法会替换全部内容, ...

  2. Hbuilder MUI 页面刷新及页面传值问题

    一.页面刷新问题 1.父页面A跳转到子页面B,B页面修改数据后再跳回A页面,刷新A页面数据 (1).父页面A代码 window.addEventListener("pageflowrefre ...

  3. 【Python】使用Python解释器

    使用Python解释器 调用解释器 python -c command [arg] - 其中 command 要换成想执行的指令,就像命令行的 -c 选项. Python 模块也可以作为脚本使用 py ...

  4. 温故知新————c++ 多态

    参考: 1. https://blog.csdn.net/weixin_42678507/article/details/89414998  (直接说明原理) 2 .https://www.cnblo ...

  5. 微服务通信之feign集成负载均衡

    前言 书接上文,feign接口是如何注册到容器想必已然清楚,现在我们着重关心一个问题,feign调用服务的时候是如何抉择的?上一篇主要是从读源码的角度入手,后续将会逐步从软件构架方面进行剖析. 一.R ...

  6. dockerfile解析过程

    什么是dockerfile? DockerFile是用来构建docker镜像的文件,是由一系列命令和参数组成. 构建步骤? 1.编写dockerfile文件 2.docker build 3.dock ...

  7. DX12龙书 02 - DirectXMath 库中与向量有关的类和函数

    0x00 需要用到的头文件 #include <DirectXMath> #include <DirectXPackedVector.h> using namespace Di ...

  8. 重装Windows系统 入门详解 - 基础教程

    重装Windows系统 入门详解 - 基础教程 JERRY_Z. ~ 2020 / 10 / 13 转载请注明出处!️ 目录 重装Windows系统 入门详解 - 基础教程 一.说明 二.具体步骤 ( ...

  9. 天啦噜!知道硬盘很慢,但没想到比 CPU L1 Cache 慢 10000000 倍

    前言 大家如果想自己组装电脑的话,肯定需要购买一个 CPU,但是存储器方面的设备,分类比较多,那我们肯定不能只买一种存储器,比如你除了要买内存,还要买硬盘,而针对硬盘我们还可以选择是固态硬盘还是机械硬 ...

  10. 多测师讲解自动化测试 _RF关键字001_( 中)_高级讲师肖sir

    1.关键字如下 1.1Get Text 1.2Get Value 2.#上下滑动(滚动条) Open Browser http://www.jd.com gc Maximize Browser Win ...