PHP代码审计实战思路浅析
声明 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。 No.1 前言 每一次总结,都可以看作是对前面所学做一次“大扫除”,就像电脑硬盘,只有将那些缓存文件都清掉,才能腾出更多空间来存新的内容。这次的“大扫除”,以PHPCMS为例,以“通读全文”、“敏感函数回溯”、“定向功能点审计”为切入点,重新梳理一遍平时的审计思路。 望大佬们多多指教! No.2 通读全文 通读全文!从字面意思来看,可以理解成把所有的代码都读一遍,把所有的代码都“读透”。那么问题来了,现在的cms大多数都是参照mvc模式进行开发,一个大的系统被拆分成视图(View)、模型(Model)、控制器(Controller)三个部分。这种软件架构虽然能使得程序的结构变得更加直观,但也只能说是逻辑上的直观,因为在使得程序结构变得直观的同时,也增加了系统结构和实现的复杂度,所以在审计这类cms时,很难通过目录结构或者直接搜索危险函数来进行快速审计。原因在于,基于mvc模式开发出来的cms不像面向过程化开发的cms那样,一个目录就是一个功能模块,一个php文件就是一个功能点。基于mvc模式开发的cms,会有一个统一的入口,所有的请求都从该入口进入,然后由框架统一进行调度,且程序中的一些可重用的操作,例如数据库操作、缓存、安全过滤等都会被封装成框架的核心类库,需要时再去调框架封装好的方法。如果在没了解程序的结构前,就贸然去跟某段代码,很容易迷失在一个个类库的调用链里。 那么,我们在审计这类cms时,首要目的就是,了解程序的基本结构。前面提到,基于mvc模式开发的cms,会有一个统一的入口,所有的请求都从该入口进入。如果我们想读懂这套源码,这个入口倒是个很好的切入点。在正式上手分析前,先来思考下,当一个请求进入这套程序的入口后,程序内部会对它做哪些操作。例如,这个url会怎么解析?程序内部是怎么将url与控制器关联到一起的?实际分析时会经常看到“Demo::test()”这种调用方式,但Demo类并不在这个文件内,而且文件内也没看到include或require,所以这些在程序内部又是怎么实现的呢? 俗话说,要用魔法来打败魔法。既然这是属于开发的东西,在正式分析前,还得来找开发取一下经。先来看下框架的运行流程。 入口文件,对应着前面说的统一入口,虽然说是统一入口,但实际情况中入口文件可能有多个,前台一个、后台一个、接口一个。入口文件的职责在于,定义一些全局性的常量,加载函数库,加载框架启动类等等。这里的自动加载类,说成类自动加载会更加合适,主要目的是,为了简化类加载的步骤,通常会将类文件的加载操作封装成一个函数或者方法,然后在框架启动前或者在框架启动时,通过spl_autoload_register将类加载函数注册到SPL__autoload函数队列中,当程序内的代码调用某个类时,php会调用类加载函数,类加载函数就会到正确的地方去把类库文件加载进来;当然也可以不使用spl_autoload_register函数来自动调用,而是在需要时手动去调用。再下一步,就是启动框架。框架启动后,可能会去加载一些配置文件,然后去调用路由类来解析请求url,根据解析的结果来调用相应的控制器,最后返回结果。 通常情况下,入口文件的位置在网站的根目录下,一般会命名成index.php、admin.php或者api.php。观察当前项目的目录及文件。 这里的index.php、admin.php、api.php和plugin.php都挺像入口文件的,这里选择先跟进index.php。原因是,一般情况下,前台的入口都叫index.php。 代码很简单,定义了一个常量,然后是加载一个php文件,最后调用pc_base类的create_app方法。入口文件、定义常量,那下面的pc_base很大可能就是框架启动类了,跟进到/phpcms/base.php。 往下翻,能看到很多定义常量,还有加载函数库的操作。 继续往下看,就看到了在入口文件中调用的pc_base::create_app方法 从注释来看,该方法的作用是初始化应用程序,继续跟进load_sys_class方法。 从注释来看,该方法用于加载系统类,继续跟进_load_class方法。 同样,也可以通过注释来了解该方法的作用。 _load_class方法用于加载类文件,对应着前面说的类自动加载,找到类加载函数后,继续分析类文件的加载逻辑。从代码中可得知,类库目录在phpcms框架目录下的$path目录,也就是/phpcms目录下,类文件名为“类名.class.php”,如果$path参数为空,则默认到/phpcms/libs/classes目录下去加载类文件,然后根据$initialize参数来决定是否实例化该类。前面的load_sys_class方法在调用load_class时,传入的$path参数是空的,且$initialize=1,也就是说,/phpcms/libs/classes目录下放的是系统类,且加载系统类库时会自动实例化该类。除了load_sys_class,还有load_app_class和load_model,三者的底层都是调用了_load_class,区别在于传入的$path不同。 除了加载类库的_load_class,还有加载函数库的_load_func,和加载配置文件的_load_config。 通过对类加载方法、函数库加载方法、配置文件加载方法的分析,就能找出程序的基本目录结构。 回到前面用来初始化应用程序的create_app方法。 跟进到application类 回顾前面提到的框架运行流程,从入口文件,到类自动加载,到启动框架,框架启动后,下一步就是路由解析,那param类很大可能就是路由类,继续跟进param类。 在param类的构造方法中会使用封装后的addslashes函数对$_POST、$_GET、$_COOKIE数组进行转义。再往后就是调用route_m、route_c、route_a方法解析路由,获取模块、控制器和方法名。 route_m方法获取m参数的值作为模块名,同理,route_c则是获取c参数的值,route_a则是获取a参数的值。同时还会调用safe_deal方法对传入的值做过滤。 路由解析完后,再下一步就是加载控制器。 继续跟进init方法。 继续跟进load_controller方法。 load_controller方法先到“/phpcms/modules/模块名/”目录下加载控制器文件,然后实例化并返回对象。 init方法通过load_controller方法获取到控制器实例后,通过call_user_func方法来执行具体的动作,最后返回结果,请求结束。 No.3 敏感函数回溯 当输入的数据,被当作代码去执行,或者会改变原本设定好的代码逻辑,就可能产生漏洞。拿SQL注入来说,当外部输入的数据被带到数据中执行,并且能通过构造特殊的内容来修改原有的SQL语句结构,就会产生SQL注入。如果我们想挖掘SQL注入漏洞,就应该把 |
转载请注明地址:http://www.leishene.com/lszx/7110.html
- 上一篇文章: 不仅连升三级,还出版个人传记,武汉快
- 下一篇文章: 没有了