在AdRotate Plugin5.2以及5.2之前的版本中,存在一处sql注入漏洞,该漏洞为FortiGuard实验室发现
漏洞位于AdRotate Plugin的\dashboard\publisher\adverts-edit.php
可见上图中,存在着多出sql查询语句
但是位于25行处,明显可见存在sql注入隐患
位于上图红框处,在拼接sql语句时,$ad_edit_id变量被拼接在id值部分。但是在这里,并没有用单引号将其值闭合
$ad_edit_id变量值可以通过get请求的方式传入,如下图
位于上图173行,可见$ad_edit_id = esc_attr($_GET['ad']);
esc_attr方法是用来过滤HTML标签的,对sql注入无影响
esc_attr方法见下图
这样就导致了一个简单的sql注入的产生
具体的payload如下
http://127.0.0.1/wordpress/wp-admin/admin.php?page=adrotate-ads&view=edit&ad=-1+UNION+SELECT+1%2CUSER%28%29%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1%2C1
这里是一个有回显的注入,数据库user被回显到name处,sql注入执行成功
漏洞分析到此结束,这个漏洞自身并没有什么亮点,不过在分析这个漏洞时,却发现了一些知识点
在上文分析的漏洞中,$ad_edit_id参数由$_GET['ad']获取
传入并拼接的sql语句中执行
如果$ad_edit_id被单引号闭合呢?例如下图
我们是否可以通过get传入单引号,闭合语句中的单引号并进行注入呢?
我们构造一个简单的payload测试下
在adrotate_manage方法中下断
可见在adrotate_manage中的$_GET['ad']中的值,此时为”-1\’”
也就是说,我们传入的单引号在进入adrotate_manage方法之前,已经被wordpress转义
具体在什么位置被转义的呢?跟踪一下wordpress的代码
位于wp-includes\load.php中
由上图可见,wordpress将$_GET\$_POST\$_COOKIE\$_SERVER中的值,使用add_magic_quotes方法进行过滤
值得一提的是,随后,将过滤后的GET与POST数组合并后覆盖$_REQUEST。在以往一些安全性不高的程序中,往往会出现,过滤了GET与POST,却忘记过滤REQUEST的情况,导致漏洞的产生。
跟入add_magic_quotes方法
可见,该方法使用addslashes方法,将传入的键值过滤,因此我们传入的”-1’”被过滤为”-1\’”,可见wordpress在入口处是存在过滤机制的
这里引申出一个思考题(问题二):
连接问题一,我们已经知道wordpress是如何处理请求的,即处理请求中的键值
如果一个插件开发过程中,存在如下的代码
插件从请求中读取数组,然后通过INSERT方式将其插入数据库呢?
在这种情况下,可以在传入的键名中加入payload,如下图
页面sleep 5秒,sql注入成功
以上代码为以往漏洞挖掘中在其他程序中发现的真实案例,有兴趣可以参见
从某cmsV4.1.0 sql注入漏洞看程序开发安全隐患
在开发wordpress插件时,会不会仍然有开发者犯下这样的错误呢?
我们的利用点位于wp-content\plugins\adrotate\dashboard\publisher\adverts-edit.php
这个文件并没有定义与初始化一些变量,直接通过url访问这个地址,显然是不行的
查找adverts-edit.php在哪里被包含
跟到adrotate.php中,位于274行处,adrotate_manage方法中的if分支里
在adrotate_manage方法中,还可以看到$ad_edit_id变量被赋值的过程,见上图173行处
但是新的问题是,adrotate_manage方法是如何被wordpress调用的,我们怎样构造url才能执行adrotate_manage方法?
在adrotate.php106行处
adrotate_manage作为参数传入add_submenu_page方法
我们来看下add_submenu_page方法
add_submenu_page()方法为后台顶级菜单添加子菜单,它的参数解释如下
$parent_slug:
(字符串) (必须)顶级菜单名称,可以在顶级菜单中加入我们的子菜单,也可以在自定义顶级菜单中加入子菜单;(也就是 add_menu_page() 函数中的 $menu_slug 参数)
$page_title:
(字符串) (必须) 这个参数是子菜单的标题,将会显示在浏览器的标题栏,默认为空;
$menu_title:
(字符串) (必须) 显示的菜单名称,默认为空;
$capability:
(字符串) (必须) 用户权限,定义了具有哪些权限的用户会看到这个子菜单(权限部分请看文章结尾处),默认为空;
$menu_slug:
(字符串) (必须) 显示在URl上面的菜单名称,默认为空;
$function:
所有调用的函数名称,通过调用这个函数来显示这个子菜单页面的内容。
回到本插件中可以看到,在adrotate中,adrotate插件注册了一个名为Manage Adverts的子菜单,当点击此菜单时,将会执行adrotate_manage方法,此时url为
http://127.0.0.1/wordpress/wp-admin/admin.php?page=adrotate-ads
page参数后面的值为add_submenu_page中$menu_slug的值
既然Manage Adverts为子菜单,那么它的顶级菜单是什么?
可见,在上图103行处,存在一处add_menu_page方法,该方法定义了该插件的顶级菜单,名为AdRotate,访问url为
http://127.0.0.1/wordpress/wp-admin/admin.php?page=adrotate
登录wordpress后台,安装好adrotate插件后,可见adrotate已经出现在菜单中,主次菜单名与url符合我们的预期
回头继续看代码,以上定义顶级菜单,子菜单的行为,都在adrotate_dashboard方法中
adrotate_dashboard方法是如何被wordpress加载的呢?
仍然在adrotate.php中
在上图77行处,adrotate_dashboard方法作为参数传递入add_action方法中
add_action方法的作用是将函数连接到指定action上。在这里,将adrotate_dashboard方法连接到admin_menu 钩子上
而admin_menu 钩子的作用是在管理员加载管理菜单之前触发所连接上的函数。
也就是说,在管理员加载管理菜单之前,我们的adrotate_dashboard方法会被加载,adrotate_dashboard方法中的代码被执行,所以我们的adrotate菜单被添加到管理页面的菜单栏中
漏洞比较简单,但是分析wordpress机制的过程还是比较有意思的