中国网络渗透测试联盟

标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告 [打印本页]

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) L" G, d6 }1 X* D- G  u& [8 I& n5 c
漏洞作者:skysheep7 s, ^; t1 f0 \3 b: j  P
分析作者:Seay
5 r6 W9 t0 O/ ]5 r( ]; [博客:http://www.cnseay.com/
1 |, a0 ?2 Y* b: W/ H$ }. b漏洞分析:9 X# [; W. R: V$ J4 S# k( l
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 D" _& ~7 E' c% o/ T' Y5 f

8 d1 ]: W/ R5 U
, G/ Q$ @, }; K
1 b# [) z  |0 `) tpublic function account_manage_info() {  . O* V0 S7 d( z/ k
       if(isset($_POST['dosubmit'])) {  
, n9 b+ n& P" s, t$ B: J8 T+ |           //更新用户昵称    ~- t) y9 }6 n0 K: E
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
$ [0 ]9 s. M! x0 _7 ^           if($nickname) {  ) F$ h6 t& j' D3 t2 o, ~
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  % ?/ U  e0 w; G2 W& `% b) u' E5 i
              if(!isset($cookietime)) {  - M( Y( c) y, ]
                  $get_cookietime = param::get_cookie('cookietime');    S( }) e% `3 |2 z% G; H
              }  0 m! b$ R" W8 ?+ B0 I; O! p- C
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  " w3 B' F! h  L  B' Q1 B
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  : F4 N  b# v+ I/ Q1 |
              param::set_cookie('_nickname', $nickname, $cookietime);  9 M& n5 }# \9 n( h3 Z
           }  
3 |$ g2 ?0 k6 f" R( H9 _, f  D* c  x           require_once CACHE_MODEL_PATH.'member_input.class.php';  % f* j, L0 W! G( i6 z& L3 F9 k
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
, Y5 j' l& p0 [3 D8 A8 b# D3 ~           $member_input = new member_input($this->memberinfo['modelid']);  0 t; `6 I, D1 V0 g: U- V+ h: h+ X
           $modelinfo = $member_input->get($_POST['info']);  
# j( i$ U( i( l$ ~3 B' W           $this->db->set_model($this->memberinfo['modelid']);  8 H) p: h# P0 A
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  3 u5 r: Y  a1 L. I4 B2 Q
           if(!empty($membermodelinfo)) {  
4 }9 B' W4 D2 X              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  " z+ x; G# s- ~
           } else {  
, [2 K7 `9 _5 F' ?              $modelinfo['userid'] = $this->memberinfo['userid'];    D6 \+ Y* k* N+ m% \7 v
              $this->db->insert($modelinfo);  5 B- |0 r$ a5 H2 @4 u- n
           }
, V( I4 n" P2 c代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,; ~  n4 S( [' F! T2 H, Y
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
" e9 t$ D5 y" x5 u' a6 m( k
! k+ m' Z0 Z: L9 \) G* z0 _& W1 L* x6 _% t

& b, P" X  h& z* z2 Rfunction get($data) {  
/ |; q  p" z$ m4 ~3 U: l" p2 R! S       $this->data = $data = trim_script($data);  
" C5 b4 X3 F6 ]6 x" D       $model_cache = getcache('member_model', 'commons');  + O6 _1 y3 y  f. G. h2 @4 O
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
- M' ^, l# p% v- M       $info = array();  . U/ m( j: B4 h' z, ^
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  " |% r; p) n% l, s* `- Z
       if(is_array($data)) {  4 {& R' c% B  q
           foreach($data as $field=>$value) {  + h7 T0 u8 \& m) x2 x
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  : P4 \! i% f' x7 j7 t
              $name = $this->fields[$field]['name'];  / O5 L' \$ P5 y& d& p
              $minlength = $this->fields[$field]['minlength'];  
1 K# i: e# O8 z, _              $maxlength = $this->fields[$field]['maxlength'];    }; m, o2 X3 |' a  l# }
              $pattern = $this->fields[$field]['pattern'];  " I5 m% q0 r1 F$ B
              $errortips = $this->fields[$field]['errortips'];  % p% l( E! P$ l/ P( Z+ t1 B
              if(empty($errortips)) $errortips = "$name 不符合要求!";  ) ^" t! n' C' L: X/ R$ @% T/ B
              $length = empty($value) ? 0 : strlen($value);  + Y: c4 l6 o$ {
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
! z" f; Q$ P) ^8 a5 r              if($maxlength && $length > $maxlength && !$isimport) {  
( b! `# j: J' _0 ?: c4 `                  showmessage("$name 不得超过 $maxlength 个字符!");  0 a4 E% H6 b/ x$ T) X
              } else {  
+ \- g+ O. M8 I. q' D                  str_cut($value, $maxlength);  
% B$ A  e8 N/ r9 L              }  3 T; Z, D. U+ t# Q4 P" |6 R
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
/ J; L; E" `/ d% H3 `4 `# s' Z% L: j                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
4 [% g& h% o3 d9 P0 E6 H! M* \7 C              $func = $this->fields[$field]['formtype'];  7 }& {/ S1 ~* U3 X' v/ _
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
6 ~* u) ]. @) C9 e. Q              $info[$field] = $value;  9 e0 q( J4 j1 x
           }  $ e$ w. U/ I9 y/ d
       }  
% i7 p2 u; s! G& C       return $info;  
$ u% o$ {+ V0 L$ s, i* v: ], a9 q    }
: p& |9 j' }' f- [trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,- U: V9 e% |$ @$ A- L4 k5 v
" k) J7 h) a6 Y! i
再到phpcms\modules\member\index.php 文件account_manage_info函数$ {# I% \8 a$ w( a  T( y& K* A
过了get()函数之后。' B% _) T' l& k: |/ y

9 j( Q) P7 }2 \8 }0 |
4 j0 v' h' E% |3 ~8 n$modelinfo = $member_input->get($_POST['info']);  & D5 a# F8 W/ ?6 I) @! N
           $this->db->set_model($this->memberinfo['modelid']);  1 U( N' Z9 {9 y& y6 U
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
( T) ?" k- v+ Q1 f! f1 F7 Y           if(!empty($membermodelinfo)) {  3 _4 @& S( i2 B% W
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
4 A$ Y4 q" j7 c0 M           } else { ; j  d' u: ^5 j. s5 U+ `1 s# o
直接带入数据库,update函数我们跟进看看
! O4 i" b2 ]; M( V8 G
/ g. s( A3 E/ O8 N 3 }; J; F$ e$ \5 q
public function update($data, $table, $where = '') {  
! m9 @% W# M) Y. Q2 C2 ^) j       if($table == '' or $where == '') {  
3 m* v! c4 u0 ^9 Y           return false;  
" D, T, t% L* J7 f+ \       }  
' Y1 `4 L) O" U; I+ T       $where = ' WHERE '.$where;  
5 n# p" f' J* k3 K       $field = '';  
% a* O+ f+ l" S0 s       if(is_string($data) && $data != '') {  
# e( |& m5 x) e2 N- N           $field = $data;  4 a, \/ v3 Q( g) @0 }2 n
       } elseif (is_array($data) && count($data) > 0) {  ; @; V* l5 S* o# O, r' W; c
           $fields = array();  1 {7 c6 t; s$ ]0 j  j
           foreach($data as $k=>$v) {  ' ?$ {7 i' A) r4 E3 V5 X8 M; Z* D
              switch (substr($v, 0, 2)) {  
( A) Q  {8 O' U1 v$ r                  case '+=':  
, e  ^- l" H  o( j                     $v = substr($v,2);  
5 c4 ~- ?/ A+ y1 {2 q% `: {) K                     if (is_numeric($v)) {  
. F7 ?9 u! D* [                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
& [) e9 N6 S( E: T7 v2 N8 L5 w# ^' ^- N                     } else {  $ X# j/ s7 I7 F& t$ B& O# q& n
                         continue;  
: V) O9 Z8 s; f: T                     }  . X/ M! P2 d: l1 ?
                     break;  ' f- B- h6 B1 V
                  case '-=':  & r' a0 k8 y6 n( ~7 R* F' {
                     $v = substr($v,2);  & @+ n5 Y; a0 N" E3 V
                     if (is_numeric($v)) {  
1 N% \' C6 S3 d                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
% u7 x& k- ~! H4 w' ?8 o                     } else {  
7 |3 L. I- {3 I6 u9 {8 B                         continue;  
, q3 L0 _' l( ~+ _- x0 X                     }  
8 \$ R5 I- b8 I0 `0 J7 s# ]( [                     break;  
) _5 E4 K) T3 R, m& ^                  default:  
. ?6 n* m$ _& t' r4 t                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
& w# i1 t+ F% i2 f' S/ M              }  * S# ^6 J/ r3 @9 {
           }  
' N* f1 _) P. n0 V6 l% h! `           $field = implode(',', $fields);  
; K2 m  Z4 B/ T+ |4 q       } else {  
" _, B# H6 o) |- q- ?' v           return false;  
8 Z7 i3 i) f2 q/ v) M# P6 R       }  7 K3 n, c2 ^: r1 a; J" s
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
+ S) ]1 W5 [' |7 ]       print_r($sql);  * R$ z9 b: M# v% `" O  _- \2 n
       return $this->execute($sql);  % f, i5 _, K, j. a' S
    } ; N9 b, I  b2 ~  K( f) h+ n
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
8 S9 R$ O% c' d- C4 I- G# L# t* Q3 j4 W# }4 N
攻击测试:
7 |' ?6 a) r, q% @: V测试地址http://localhost
2 Q2 S% }4 ^6 _. K: V" e. a  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 A. X7 A* p* S& ?
$ e. r9 `  ^8 b" N) J" w. z[attach]179[/attach] - W+ F/ n' a, _4 t

# {2 C( @" V/ L  o& d2 f! A: q* O4 G3 K. E2 n
[attach]180[/attach]
. b- m" ]* R- S0 M# k) Q6 i& K




欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) Powered by Discuz! X3.2