|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告/ u! A! H- S; |- L5 ~( R: t$ @+ E2 y7 M
漏洞作者:skysheep
$ O9 n! i: T a6 A" `2 H' C1 `分析作者:Seay" s# l2 c5 I K4 I
博客:http://www.cnseay.com/
' E- h% D m3 x) ~1 Z7 `% ?/ W) q漏洞分析:# r/ U& M1 R6 K/ u+ U3 }+ c- {! M
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。3 d8 m* [" ]8 e, @6 K& L0 ~
1 T5 ^6 M. e% n# d: E3 X
$ M+ \2 D( ]9 V; p2 t* b8 _ b
; M3 K4 t8 R- i, H! {public function account_manage_info() {
i! w4 ~4 `* p7 E; H# u: D if(isset($_POST['dosubmit'])) { " R x' `: R. |: x. y8 @" R }, y3 j$ T
//更新用户昵称 ) }. A7 j3 B7 e
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 9 } n. D9 U6 n! X
if($nickname) {
6 f) G# D t+ K, e% Y $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
. I$ y: k m2 |& A3 E, ^2 Y if(!isset($cookietime)) { O H. i) b0 Z" I1 U; g/ }+ M
$get_cookietime = param::get_cookie('cookietime');
) E) K4 S- ?. Z! B p" k; w } ( k2 ]4 M, w* h& l' Q$ j
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); & ^* {; `: C, ?( X0 q* j3 t6 B% z
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
! F8 f) Q& |6 S$ X) E param::set_cookie('_nickname', $nickname, $cookietime);
9 ~- x# R8 i0 L+ p* m: R }
+ v. I+ j# `2 Y1 y; b" C require_once CACHE_MODEL_PATH.'member_input.class.php';
* k, D9 e: @9 c/ o1 r, V1 L require_once CACHE_MODEL_PATH.'member_update.class.php';
! R; Q5 \! l2 c. m4 j $member_input = new member_input($this->memberinfo['modelid']);
. Q+ F! }5 ^+ n3 y: K9 L $modelinfo = $member_input->get($_POST['info']); ) o+ @8 ~/ d( `3 G2 X5 O
$this->db->set_model($this->memberinfo['modelid']);
9 F( }3 M# @9 S/ L/ @ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
9 T3 k0 |( E9 H! o. |- o if(!empty($membermodelinfo)) { 5 y4 M& b2 R- p# P: N4 L7 y( C
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
O2 e$ |7 x4 [+ C) w6 ] s } else { - x$ i' g4 I3 L6 K5 c8 }2 Y4 [
$modelinfo['userid'] = $this->memberinfo['userid']; + b. }7 F& x& I" y9 y/ |
$this->db->insert($modelinfo); 2 `4 y1 `4 z/ y
}
# ?( |9 A$ G; u2 H+ O代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,( m+ |0 I( m0 |5 }7 r8 ?8 G* b
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
X" _5 K p/ s D% ]" E
# |* u/ \: [9 _( n1 w& B. V& a" K
# i$ X: E- o- `$ a! F; r
8 I% B) Q2 \4 c, Y( @function get($data) {
0 E& P' S! C* K4 u) R0 p0 S3 v $this->data = $data = trim_script($data); # ?: x) n: A; C/ Q9 R( d0 d9 o
$model_cache = getcache('member_model', 'commons');
4 t: h# N9 s2 z( Q% _ $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; * ^$ {. ^6 x j4 X' e
$info = array(); " r( j# H [: d, ~$ |& Z: V
$debar_filed = array('catid','title','style','thumb','status','islink','description'); # p# R% @; B* e5 t/ S
if(is_array($data)) {
1 d8 z" E+ s0 }7 |6 F1 D- G foreach($data as $field=>$value) {
2 V+ _+ V) n' r4 J: n if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 6 y( Y ~: N: I$ ]: U2 L
$name = $this->fields[$field]['name'];
3 w! ~- Q4 J. B) L. D* L $minlength = $this->fields[$field]['minlength']; " {# `( A* H+ Q6 S
$maxlength = $this->fields[$field]['maxlength'];
9 p4 i5 P, j2 r! U; o $pattern = $this->fields[$field]['pattern'];
) a0 [1 _* ^ p1 a2 M1 Z $errortips = $this->fields[$field]['errortips'];
/ h4 }6 B+ D9 T' j6 B/ H$ j if(empty($errortips)) $errortips = "$name 不符合要求!";
( Z) e. S9 u7 \4 w3 p; f) S $length = empty($value) ? 0 : strlen($value); 0 c K5 A; t+ K0 T A+ r$ ?
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 3 A+ d% N8 q* L6 w/ Y. C! n
if($maxlength && $length > $maxlength && !$isimport) {
% E3 A$ J' q. P8 u& ^ ^$ j showmessage("$name 不得超过 $maxlength 个字符!"); 3 x* g5 O( b$ B$ M. \
} else {
/ K' p9 P: f8 \ P" r- G B' s1 w str_cut($value, $maxlength); ( L2 Z: ]- V6 v+ x8 n$ G6 E4 \
}
. h* M- }8 z; _7 c1 I: l! X if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ' ^0 t( ^+ X! r- o9 @& z! j* e. f
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
! W6 J5 w" E& B3 c $func = $this->fields[$field]['formtype']; 9 `* I, z1 L- f8 ]
if(method_exists($this, $func)) $value = $this->$func($field, $value);
' z$ r; ?# z' S- m* |1 S $info[$field] = $value; - B; f6 u. y7 x5 e
} 4 w1 B3 A$ j1 W! R% G5 U# W
}
; k/ X h* A$ G- y1 G0 K- r return $info;
# H6 y9 F, p/ d: l: n* s } & e" x- p5 U- ]) e4 H! N4 P
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,0 `4 U2 C& X. S' i
+ z! d% S2 r& I. u再到phpcms\modules\member\index.php 文件account_manage_info函数$ \2 ^- V- d. E6 y* S6 V# O
过了get()函数之后。, `! @) r- k' X8 q
4 T& X8 L% E( c& y, ~ - b1 M+ T7 H+ h1 n
$modelinfo = $member_input->get($_POST['info']); , f5 c4 S4 J; \! b! }5 @
$this->db->set_model($this->memberinfo['modelid']);
* V' Y; g6 |1 _ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 7 @( E1 {) M: g9 ^% z3 _: e
if(!empty($membermodelinfo)) {
6 n% r- x5 ]* ~1 \6 n $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ^8 z1 Z- j3 A2 i5 p
} else {
& B ?. g" d# Z D2 G: W5 Q直接带入数据库,update函数我们跟进看看
) Y" ]! u5 e0 r. N' x
2 h- @; y+ ^& u' s$ N ^ 0 s5 q7 F2 f. Z2 `. C
public function update($data, $table, $where = '') { ( {" |* p( G/ ? w. c
if($table == '' or $where == '') {
. J; d4 l3 ?" W/ T F7 s return false; " t9 W/ Y k: A' d. E9 [
} - h% ` z5 b* ~, V; f
$where = ' WHERE '.$where; 7 I" @. k V) _- g4 f7 z+ j2 l B6 s
$field = ''; 2 W0 r( i& x% l- j1 t$ W
if(is_string($data) && $data != '') {
4 P! {: Z% a( Z) R; q $field = $data; . G0 f4 @& F& q/ e/ b
} elseif (is_array($data) && count($data) > 0) {
9 i, t! T5 k% c; C2 b $fields = array(); 1 ?( l2 [5 y4 s: Y( k
foreach($data as $k=>$v) { $ @% K5 H8 R0 Y8 a1 o
switch (substr($v, 0, 2)) {
% \2 C0 Q8 S4 W case '+=':
6 D! P. i4 [) i. B. B/ H $v = substr($v,2); % j8 f3 I/ X; k Y$ W6 K
if (is_numeric($v)) {
/ w' ]6 ]* ]) e $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
% t: r% B* { J: n$ |& N# \ } else {
3 Z; `1 X, [" \1 X continue;
* o& {8 y5 G) }& T } 3 W& X! I5 {; }! ~- C# v o
break; 1 _$ \8 Q- c3 l5 G" h
case '-=': % D V M# U+ x. z, @, V: i" z& n
$v = substr($v,2);
1 b' a: x2 n* Z if (is_numeric($v)) {
: H" p6 B- b# v& u- X $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ( l/ m$ \7 l$ k2 L/ G5 K
} else { $ ?5 \/ q. }/ h! O
continue;
" L5 g3 `2 y f! F. | }
: V, o; m- [3 K+ E2 w: R break;
, K- Q4 d$ g. O) m default: & N& l2 M7 X9 M0 e
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
) r$ c' r' K% I4 C0 p$ l# \3 T } , ~0 c( `! d' g
} ) x ?: F% B% B. H
$field = implode(',', $fields); $ P+ |1 k$ b7 ], C: q
} else { 9 S- z# w4 t' _( U' O
return false; " k9 S. \" G T( d/ Z- H5 G3 w
} $ A; U! w( T5 I+ e8 u# b; |
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
3 u# P7 O: ?2 D) x7 u, B+ p! \1 D6 F print_r($sql);
: ~( v ]. R) P% t8 J% Q return $this->execute($sql); , @) O" e( [/ i" p# }$ S+ E8 R# X
}
" y+ e7 W, `1 i从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。8 O5 n1 ^' z9 |) y3 g. F$ c7 d
1 @" C/ w6 v0 e5 u R4 J8 z攻击测试:
9 w6 k' q% [3 T' z; W5 G" P测试地址http://localhost' O: W: f: {; p8 c/ X
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
7 r2 h3 ~0 T. b3 d! g; Z7 r( j5 o# S+ a* Y
7 N& D7 _) {4 O8 v& L5 j; @" `8 t |& H7 c2 g! t W
6 N( x) k0 P- }) A" {4 Q- p. f e _# X
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|