找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 1973|回复: 0
打印 上一主题 下一主题

phpcms v9 2013-02-01 会员中心注入漏洞分析报告

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
' N( c! A1 X" K9 C3 Z漏洞作者:skysheep+ P5 h& o1 ]9 c
分析作者:Seay
4 t& A( {( \. o1 ?! b4 m7 V3 a博客:http://www.cnseay.com// j( d1 `9 q1 p+ E0 u  H3 I# |
漏洞分析:
6 }- I2 L+ c% i: ~# o  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* Z8 W7 B; Y7 m4 Z% W  ^
, F  e4 O7 y) P# G4 d4 m

2 u7 n$ }5 U. }/ p; r" N# J7 B. { , P- m2 N. I) Z' m
public function account_manage_info() {  
6 _' ~) [8 D0 a5 p$ r/ C9 g       if(isset($_POST['dosubmit'])) {  
/ o, h* `. h( Z% m           //更新用户昵称  
& z% M2 f/ H7 G. C. H           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  7 i& G$ v$ m! v: M& a  F# w* ]
           if($nickname) {  
+ Y) K1 Y5 y& e3 _2 q# o0 g$ K              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  2 {4 J: ~9 W! Q9 z6 B
              if(!isset($cookietime)) {  
; _- K+ `" V  y* f/ [                  $get_cookietime = param::get_cookie('cookietime');  
+ e9 `' Z# W+ R+ a9 W4 }              }  ) c- _" ?+ ]- I! C0 c; O
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
3 e( j- a6 M& r2 ]              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  5 j, ^- p8 s" m& p( V
              param::set_cookie('_nickname', $nickname, $cookietime);  
2 I1 K* W" N9 |% u  \! q+ u4 M9 a. L           }  
! m* o3 O  ?' s0 U- S6 `           require_once CACHE_MODEL_PATH.'member_input.class.php';  
6 d/ A2 h+ d8 P( V3 y! p. \& |           require_once CACHE_MODEL_PATH.'member_update.class.php';  
6 J5 |; K1 }" b$ v) a           $member_input = new member_input($this->memberinfo['modelid']);    R6 m3 A0 N  z# s5 i
           $modelinfo = $member_input->get($_POST['info']);  
4 Y8 R/ I% G- O. b           $this->db->set_model($this->memberinfo['modelid']);  , \1 l5 V# K4 k: w3 e
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  3 h, I: ]+ d& s+ H- Q
           if(!empty($membermodelinfo)) {  . R, C  ^. \$ y' S# a3 l
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  5 R+ n1 n' S0 u; X# `
           } else {  
" ]# ?% P% F  `( e              $modelinfo['userid'] = $this->memberinfo['userid'];  
0 q# c. e5 l7 i- g% o5 m              $this->db->insert($modelinfo);  ) z) H+ t! J1 r+ G# v' m0 f
           }
0 _; b5 |! v' e8 Q& V代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,0 H, \* L7 B' y$ x5 t- R
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( j0 Q5 @" G8 v1 j; G' C" d- }+ a, h* P$ c7 s
2 a! b3 h8 C. U- A9 i; B% R
/ ], ~# \$ {) ~* w: f6 A
function get($data) {  0 N$ \0 ]5 f( K) B2 e5 M
       $this->data = $data = trim_script($data);  ) g% ?' S: t$ t4 ]
       $model_cache = getcache('member_model', 'commons');  , b$ b( c2 @3 G! t  {2 d
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  " j) _- {0 |8 @- R
       $info = array();  
( P9 C! y4 ?" ^! ^  g       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
9 w, }, H! y+ M       if(is_array($data)) {  , l6 G5 }6 z% h" t. `) x8 J
           foreach($data as $field=>$value) {  
/ J" Q/ @9 t+ w& a& w9 e              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
3 t6 e4 N6 F1 {4 _3 Z/ L              $name = $this->fields[$field]['name'];  ) C) u. G; u7 G
              $minlength = $this->fields[$field]['minlength'];  / o. w: @! S" ]/ E2 l6 ?  g
              $maxlength = $this->fields[$field]['maxlength'];  ; w- R7 \! D% C& b! d5 @) R9 T
              $pattern = $this->fields[$field]['pattern'];  
+ c# |' X' \7 x9 p4 V7 [8 Z              $errortips = $this->fields[$field]['errortips'];  
! Q% ~6 p9 d( g              if(empty($errortips)) $errortips = "$name 不符合要求!";  % i' c7 [3 O5 G1 o. a+ U
              $length = empty($value) ? 0 : strlen($value);  
. P) h! t. Z& g6 I3 t7 Q              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  ; Z( B& l, Y( O6 z
              if($maxlength && $length > $maxlength && !$isimport) {  , |. X0 {! U+ `0 N8 r4 t
                  showmessage("$name 不得超过 $maxlength 个字符!");  # d& ^" Y+ h1 @
              } else {  
) @& q, G  M# |                  str_cut($value, $maxlength);  
" ~6 H* @# W5 i( E; M              }  
9 w6 w7 V4 u! z9 n& @0 l0 W9 M+ L( f              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
( }3 Z& v! e, [+ X% K- G& y# f4 x                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
# G, i5 V! U( S5 f. Z- p3 }              $func = $this->fields[$field]['formtype'];  
3 C5 h, f+ u& }% Q: q' k              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
  a% E- c4 }  l" }& G' ^1 n; i              $info[$field] = $value;  
, `, }6 K% y/ M( S           }  
& n& M1 {3 V$ q* i       }  2 U, }, b# ?. i' V7 m
       return $info;  
0 @; D: |1 ~2 V) N; k8 p9 N7 l    }
& J5 C2 U/ v! Rtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
. d. ?0 n) @. G4 d8 e! G5 H: m& w, u
再到phpcms\modules\member\index.php 文件account_manage_info函数
  E+ {5 r! q# W过了get()函数之后。
8 m' W/ t/ y/ {
7 k8 ], F4 X$ H" `) U 0 J/ K' _* l7 \1 b) @( j3 I
$modelinfo = $member_input->get($_POST['info']);  # p- e; T) X  j2 m6 J: o: K7 J
           $this->db->set_model($this->memberinfo['modelid']);  % e' n0 b9 ^$ |6 |0 X  U
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ' G' x7 c7 ]% C2 c5 C8 o9 v' N" t
           if(!empty($membermodelinfo)) {  
2 M, B. ~2 r. A- }+ I$ t( i              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
/ ^& S+ q$ S3 i- r9 d           } else { 3 M' z: i! T, s/ O$ v5 Z2 c
直接带入数据库,update函数我们跟进看看# P  _4 P1 Q. w3 v% J
/ L! W2 H* s* D2 u( J" A
4 q+ q; w  `0 F) G8 w
public function update($data, $table, $where = '') {  
  B+ q& r) w% j+ n7 Z  F       if($table == '' or $where == '') {  
+ E( j; `) l' |0 R, t- Q) s' u: \           return false;  
( m8 f7 n7 E5 @+ h+ i* x       }  
% U* X% v, s" y- q       $where = ' WHERE '.$where;  / j- q" ]) Y: V3 ^
       $field = '';  8 K0 R2 f5 d  t8 \- N% e
       if(is_string($data) && $data != '') {  8 g1 q$ ~" s' x9 Z
           $field = $data;  
  u" X& p7 Q1 z9 h" G       } elseif (is_array($data) && count($data) > 0) {  
8 z& Y7 E: \& t# }& D" `* h7 T4 d           $fields = array();  
9 W1 e: s" D/ d. L  L" C- S           foreach($data as $k=>$v) {  
" o& j5 h4 i5 E3 Y( d3 l5 w0 Q              switch (substr($v, 0, 2)) {  * T# \5 ?' o4 M! o
                  case '+=':  
# t( J5 n, }  ]: z8 O. A1 N5 k                     $v = substr($v,2);  
2 O6 I" L- r9 l4 R( u' i                     if (is_numeric($v)) {  
3 f( G. q4 e0 L. K( J. \                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  7 i+ ]. Q% a; j
                     } else {  ' I/ v$ m# c1 K9 O- O
                         continue;  1 w$ Q2 S6 S0 Y! G9 U
                     }  ' p7 ?# |  d4 N! B
                     break;  
+ Y# J& a+ ]9 X" O5 F& N: v0 z                  case '-=':  * ?& v2 ?  ?$ z
                     $v = substr($v,2);  . W7 S6 m* k% L' z0 k. J
                     if (is_numeric($v)) {  
$ e; G3 x( d. x& @  s- {                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  7 u% L) M! U1 @) P6 X, A$ F" l
                     } else {  
8 ~; g% a! c5 I/ j" k" e                         continue;  ' D4 M+ L+ q& c* r% b) r- w$ `/ s
                     }  : _: v( {: F7 ?
                     break;  
# ?# }: h5 g: |) O9 R3 r+ }3 @                  default:  
7 w1 L" n1 z9 S3 |+ S                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : r  q' l* g* Y
              }  / o2 l9 e% Z8 I
           }  / t/ [( S3 a" x& Z
           $field = implode(',', $fields);  ' Y9 |' D  V7 }
       } else {  
! Q9 Y" ~& A: Z+ A# h           return false;  0 i; i/ x! x4 {2 q3 c- X+ F
       }  
+ q! \* k: v* X& j( _( ^       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
( H3 R: F4 a7 O       print_r($sql);  
+ D0 ]. p, y+ Q4 A7 C       return $this->execute($sql);  
: L' V- k) Y, T: }$ t9 n    }
& m: [: G: v! B. L5 N从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 D$ w/ O* \- _# H+ K) V
# O5 j2 {/ ?' e攻击测试:% E' B1 k& n% x: \/ W
测试地址http://localhost0 n, ]. w* _, k
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 ^, a& p4 ^) R% h5 K/ S) g+ B
% b) C$ q5 ^( a5 o2 m7 J$ }7 L
; _& M6 e, k3 W3 O. [3 ]3 N- R6 s. J2 Y* x' S9 h- R

6 V* R! p0 \8 Q' v
- M# ?  x8 ]: |$ M

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表