|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
! v: k2 L8 E) y4 V2 V漏洞作者:skysheep
: l4 j# V& f) K+ a. {, Q分析作者:Seay% w5 M9 Q+ ^' E
博客:http://www.cnseay.com/# B; H' L1 B6 Y( i4 {* u
漏洞分析:
0 P7 B2 b1 [1 ^0 \8 \$ [. p 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
3 y$ \. y3 C/ f1 l2 N7 L4 j! ~. t" h6 P3 u
' i; A* [, ~/ g- C4 M8 s M$ B
, ?6 `) }4 A, N. x6 Q7 Vpublic function account_manage_info() { : U% O- _; @ T$ E: X+ u% H6 | z
if(isset($_POST['dosubmit'])) { N& z6 g/ @- \4 Z7 S! X
//更新用户昵称
0 P5 O# }% z$ t! Y2 D $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; , n- X. U9 y3 t; V C7 Y* F& N
if($nickname) { + w5 Z U3 f' M) @
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
. E9 X+ A0 `; {7 G- v if(!isset($cookietime)) { $ T+ |8 y1 i, Z
$get_cookietime = param::get_cookie('cookietime'); ! s( u3 g! G8 C% ~+ G- H9 h) U$ f
}
/ Q( y& f, Q$ y# t* q $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
' y5 v2 L! V: |4 l: ^ $cookietime = $_cookietime ? TIME + $_cookietime : 0; ' M* P" X' f9 t5 H
param::set_cookie('_nickname', $nickname, $cookietime);
) R1 u9 {3 {5 C3 h9 q } ) O2 R0 x' D8 S9 j& r
require_once CACHE_MODEL_PATH.'member_input.class.php';
( s8 x- N% t. c. O" n& X6 A8 ] require_once CACHE_MODEL_PATH.'member_update.class.php'; 8 a W2 R9 R- k& V
$member_input = new member_input($this->memberinfo['modelid']); $ y1 A$ B6 }& |9 R
$modelinfo = $member_input->get($_POST['info']); 0 Z9 K0 ~ S7 r5 Y0 T# I* ~" w8 z
$this->db->set_model($this->memberinfo['modelid']);
$ \( G2 j% |, T( B5 P $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); . ~6 a" E! c k9 T9 |
if(!empty($membermodelinfo)) { # b, s! ?' f2 I/ \: N2 s8 u
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ' h& e5 |, Q" k; H3 k( b) ]7 f7 @- A# I3 I
} else {
3 S* x; ^( E* T% u0 v } $modelinfo['userid'] = $this->memberinfo['userid']; 3 l* I, y/ F1 ~0 z3 W- G
$this->db->insert($modelinfo); 3 Z: n% q9 j- q
} ) ?- h* ~( G/ o$ |. L3 {
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,2 m. Y1 e: a7 l* C. I5 p, V! W' C3 G& V
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
F% i" t) _2 F3 W. L
% n: [ k+ u, {5 `9 ^3 R6 f4 w9 K: \6 ~' F. Z, y1 \9 L, M( O1 H
_6 x, H" y% E: }% w
function get($data) { ; ?; j: J7 ]1 ~8 J$ `& q8 N) }+ R
$this->data = $data = trim_script($data);
. o4 E* N. Q; [) T! p $model_cache = getcache('member_model', 'commons');
2 N" B# s" _( N& ?1 T7 K7 { $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
! a9 e- C: O& N% n $info = array();
1 n8 M6 }5 n5 {& L6 V $debar_filed = array('catid','title','style','thumb','status','islink','description');
/ q/ R- A. _ i$ u. y( C; k if(is_array($data)) { $ a7 T' z) q8 U6 F& s1 e
foreach($data as $field=>$value) { ; v0 I" q5 Z3 z
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
1 b; ^) Z+ A! Q* N+ g3 ?: x $name = $this->fields[$field]['name']; 1 `/ D4 V1 q4 F$ G6 s7 ?
$minlength = $this->fields[$field]['minlength'];
7 d5 `7 J, J: y0 Y4 `6 w $maxlength = $this->fields[$field]['maxlength'];
5 Q! V* a. c' c; C5 r $pattern = $this->fields[$field]['pattern'];
9 @* F- h. o* @5 Y $errortips = $this->fields[$field]['errortips'];
' D: U' w% H5 c" w8 n! f if(empty($errortips)) $errortips = "$name 不符合要求!";
7 _+ {: Z3 x6 C $length = empty($value) ? 0 : strlen($value);
4 Q' l+ ^ i# ` if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); / r6 d3 z) D1 c$ j: w
if($maxlength && $length > $maxlength && !$isimport) {
3 z1 f; L5 k3 j$ U9 T showmessage("$name 不得超过 $maxlength 个字符!");
4 V# ]0 E) G P( q" q } else {
0 n& {# y2 v5 A" @" |, e/ B3 |, G str_cut($value, $maxlength); # Y4 \9 [7 g9 S0 `3 g
}
4 V8 Y4 Z0 E9 P U1 R" D& C) d if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
9 i" W' R5 o7 C' @, ]0 X3 J if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
' E$ Z: s+ C+ T $func = $this->fields[$field]['formtype']; 2 {) F1 Q! F5 w8 `& e
if(method_exists($this, $func)) $value = $this->$func($field, $value); 4 E$ M$ u5 o6 |3 h! m+ l2 y
$info[$field] = $value;
- F: W& m" ?! o, l9 Z# G% j } ) I7 F5 I$ C* t' S7 ]- e( ?1 a0 ?
} ) E& x- O# u, ~* Q$ K) I
return $info; 7 i6 `, W- n- k9 n" a" A p: G
} , y8 z3 b' X# B2 y: Q. k1 H( m5 x6 ?
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,# K' F4 `% r5 f$ v
# q; _+ m& u9 |7 R; P, F
再到phpcms\modules\member\index.php 文件account_manage_info函数
5 J! w; m. h4 q) Z, q' L' s/ o$ ?( ~过了get()函数之后。% @6 E- u& @ m
% H& q" X6 w' Y2 r2 Y / Q5 y5 i( g- `* |5 D7 |$ I
$modelinfo = $member_input->get($_POST['info']); S* Q! h- Y: f6 k" n, _
$this->db->set_model($this->memberinfo['modelid']);
' p6 l4 a8 r5 H $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
0 ]3 @9 @! K# s/ d, D* i if(!empty($membermodelinfo)) { , V* l% Z: h$ ~+ p8 l! o) m y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
3 _. N' Q" |1 @1 G } else { 8 g) b7 |9 F. O* ]6 X4 {, b
直接带入数据库,update函数我们跟进看看
& r0 z! t- {7 N4 h' y
2 ~% ^1 Y& H8 a6 H4 I* o : p7 g; B9 F. d4 N9 y
public function update($data, $table, $where = '') { H! ~" q/ W5 w/ e
if($table == '' or $where == '') { " b% e8 `+ a! \- I& D
return false; ' H9 w) _9 |# m9 [6 J/ T
}
2 S# N" K8 ~5 g8 i $where = ' WHERE '.$where; ( m1 A& T% u; F2 I) r4 |" c/ H
$field = '';
* U& a1 j4 C6 Q& F; x$ e if(is_string($data) && $data != '') {
% i' B4 f& S8 `$ y $field = $data;
, c3 Q2 S' f/ M4 P H1 r+ A } elseif (is_array($data) && count($data) > 0) { ! |, b: l6 a3 | r' l( h
$fields = array(); / {+ t7 i0 Q' o- M3 o
foreach($data as $k=>$v) {
3 n( G3 ^" \/ V; ^+ h% e switch (substr($v, 0, 2)) { 6 I& l$ _" L1 C
case '+=': 9 @8 x% \) D) G% d( w
$v = substr($v,2);
; a0 Z8 Z: o* }3 v) H6 [ if (is_numeric($v)) { ! w3 |* Y4 V) ~. Y8 [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 0 F5 N) L8 ?; F- E: a
} else { - G# N! n0 w8 W: G. _0 i8 y
continue; : q7 \7 S3 f5 D) n" I+ |
} $ q. S/ T5 x" h1 m6 o
break;
$ L0 G3 }4 j( M8 E4 K case '-=': & |8 I3 v- ], ~) b
$v = substr($v,2); 6 X6 l- x* V0 e4 F
if (is_numeric($v)) { 2 a: r# v4 ^, }) m
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); w* f& |" R+ [; f3 Q) q5 ?( f
} else { * G$ K3 t) R1 a/ t6 t2 s9 t
continue;
1 d) a& f9 c7 B" p; v8 e! Y. g }
8 }( v' b% S3 v/ g, ]1 e break;
' E, }8 ]$ Z4 W) n+ X2 i E default: 6 \. S* N# w* n4 X' J+ {
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); $ p0 q0 z/ E: l0 Q. W! @4 G# R
}
; a, Q8 \$ F/ Y: V+ P } 2 O3 @+ [# y) X& C) Y6 e
$field = implode(',', $fields); 5 p) f5 M9 O T6 }+ v2 k
} else {
. U/ T; n+ e, J, _ return false;
+ M& u$ K; N, p7 H5 z }
@* M( X& p8 d& D+ s4 ? $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
: b } a ~" _- t4 L print_r($sql);
0 J$ l3 B! U# J' ^% h6 \& U) n return $this->execute($sql); 3 O* _! J. G+ L$ k P0 o
} 5 j7 M0 y( @/ f9 i
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& N4 F" r& [8 a! B& O( _ a1 n0 C% c( o5 e, b
攻击测试:+ C3 [5 @; K& }& Q5 V! m- `
测试地址http://localhost7 t+ k, @- {$ v! q/ R2 s( S
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
5 U' s& \+ G4 @8 A$ U: a6 e
5 O9 I" j, U4 n3 K
. \: A2 Z' H9 D4 E x. H% ^+ l' p8 C! E" y1 h
; B& Q) A- e: e. F3 }
9 R5 Q, e) ^& s5 L: Y0 [7 H8 [ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|