返回首页
EN
H7 / SECURITY RESEARCH NOTES

ESPCMS所有版本任意用户登录

本页导航5 个章节

前言

将《代码审计-企业级Web代码安全架构》这本书提及的所有漏洞案例完整详细复现,并提供源代码包,方便后续新人从这本书入手的时候自己复现案例才能学的更深刻。


漏洞标题

ESPCMS所有版本任意用户登录 书P65

准备工作

源代码包下载及安装什么的就不说了,以及安装时遇到的坑也不说了,因为都在上一篇复现该cms的文章 提到了,这里不提第二遍

开始

该漏洞位置出在/interface/membermain.php文件中,interface目录属于根目录下,和index.php在 同一个目录下。再查看和membermain.php同级下的其它php文件,发现它们定义的类名称都相同,由此 可以猜测可能和interface同级目录下的index.php是入口文件,然后实例化的inteface目录下的类 文件,调用相应的方法。既然是猜测,咱们待会儿求证,先看membermain.php文件源码:

image.png

分析in_center()这个方法,第20行-21行代码包含了一个client.php文件,它的路径由admin_ROOT 常量拼接后面的public/uc_client/client.php,我们发现该应用根目录下也有一个public目录 然后我们打开该目录,发现能找到client.php文件,如图:

image.png

那么现在基本上可以肯定就是包含的这个client.php,那么这个所谓的admin_ROOT常量肯定是指上一级 目录的路径,然后咱们先看一下和interface目录同级的index.php,也就是应用根目录下的index.php ,看是否存在admin_ROOT常量的定义,如图:

image.png

我们可以看到定义该常量时用到了dirname()函数和__FILE__魔术常量,通过查官方手册咱们可以知道 __FILE__魔术常量返回的是当前文件的绝对路径,dirname()函数返回的是当前文件的父目录,也就是 当前index.php所在的目录,也就是应用根目录,结合刚才membermain.php中包含的文件client.php, 刚好能对上。咱们再看一下index.php,顺便把怎么调用membermain.php也讲了,如图是index.php

image.png

image.png

上述源码基本跟上一篇espcms漏洞分析的入口文件一致,这里就不多说了,怎么调用基本就知道了。 现在继续回到刚才的/interface/membermain.php文件。可以看到第23行代码静态调用了父类的 start_pagetemplate()方法,查看该源码:

image.png

看名字结合源码就知道是调用模板的文件,不用管,接下来继续,第24行代码调用了父类的member_purview() 方法,查看该源代码:

image.png

发现这里定义了几个类的变量,其中的这个$ec_member_username和$ec_member_username_id和 $userinfo变量就是导致漏洞的关键变量,还有intval()这个函数。就先看到这,我们回到membermain.php 文件,继续往下看,就不一行一行解释了,看过上一篇复现,这些都讲过。第29行代码构造sql语句, 可以发现其中涉及到两张表,一张是espcms_member,还有一张是espcms_member_value,两张表 做一个左连接查询,这里我们结合表名称,表结构,如图:

image.png

可以猜测这是关于会员的表,咱们登录网站后台可以在导航栏看到会员的选项,点击,咱们先增加3个会员 这里分别是test3,test2,test1,然后发现espcms_member表中增加了3条记录,但是espcms_member_value 表中没有记录,这里不用管这个espcms_member_value表,因为用的是左连接查询,所以必定会返回 espcms_member中的所有数据,如图(两张):

image.png

image.png

然后继续回到刚才的第29行代码,发现where子句中引入了一个$ec_member_username_id变量,这正是 第24行代码调用父类的member_purview()方法时所定义的变量,我们再次查看member_purview()方法:

image.png

可以发现,该变量是从通过accept()方法接收的,这里不提accept()方法了,上一篇文章提过了,也就是 这个变量是直接从cookie中提出来的,然后拼接到了sql语句中。但是的但是,这个cookie是加密过的, 这里出问题的并不是什么没加密呀,导致cookie可以猜测什么的,不是的,这漏洞真的很有趣。 刚才咱们注册了3个会员,咱们在前台登录一下,如图:

image.png

正如刚才所说的,可以看到url上传递两个参数ac和at,分别是控制器名和对应的方法名,并且请求的是 咱们刚才说的index.php文件,就是开头说的那个index.php。所以咱们可以知道,登录后显示会员个人 信息的方法就是调用的membermain.php文件中的in_center()方法。咱们前面提到in_center()方法 查询用户的时候是拼接的是父类的$ec_member_username_id,咱们看父类的这个变量是怎么得到的,如图:

image.png

咱们可以看到这里从通过accept()方法从cookie中接收了两个变量,$ecisp_member_username和 $ecisp_member_info,然后将两个变量解密后赋值给了$ec_member_username变量和$user_info 变量(这里的$ec_member_username是采用类属性赋的值,所以可以继承给子类,而$user_info只是 普通赋值,只能在该函数中使用),然后看到第421-422行代码,这里的list()函数意思就是将$user_info 变量中的值依次赋值到该函数参数中的每个变量。首先赋值就是$ec_member_username_id,这里赋值 的$ec_member_username_id变量还是普通赋值,重点在424行代码,用了一个intval()函数将 $ec_member_username_id变量转换为整型后再将其赋值给类属性$ec_member_username_id,这样 $ec_member_username_id也就继承到子类了。问题就出在intval()函数,先看手册,再搜一下, 手册上说返回变量的整数值,更准确一点是,当传入一个字符串时,返回的是字符串中第一个数字的字符 之前的数字串所代表的整数值。比如intval('test3'),它返回的是0,但是如果是intval('3test'), 它返回的是3。那么利用链就来了,cookie是加密的,咱这个解不了,但是前面说了,它接收时是两个变量 一个$ecisp_member_username,一个$ecisp_member_username_id,它们加密方式相同,那么我们 就可以通过注册一个名为'3test'的用户,此时将cookie中的$ecist_member_username的加密值替换 到$ecisp_member_username_id处,然后该$ecisp_member_username_id经过解密并且intval() 转换后会变成整数3,然后拼接到sql语句,导致查询出id=3的用户个人信息给了前台。如图:

image.png

image.png

image.png

image.png

image.png

如图可以看到我登陆的是3test用户,修改cookie后刷新页面再次请求返回的是test3,即用户id为3的 用户就相当于我可以 无限访问数据库中的个人信息,即造成任意用户登录。所以到这里,可以发现它并 不是简单的任意用户登录,比如cookie没加密导致可以构造cookie来实现登陆,这里利用真的很有趣!