|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
: D* ^' n7 C# X" z漏洞作者:skysheep9 Q/ l5 ?; H5 t- A
分析作者:Seay2 R' q& p9 l4 n& n
博客:http://www.cnseay.com/! n. k8 k' l/ S
漏洞分析:& H+ B8 F- l3 [& M3 m' w
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; a4 R7 H! Y5 u6 T6 N0 c$ U+ N! w) J
; G, \* n0 n8 g0 C( }
+ {" \ e4 g( z7 D0 _
! t( \: r' e0 s# b% L$ L
public function account_manage_info() {
: r9 m. l4 M2 d, A) @: R if(isset($_POST['dosubmit'])) { : a1 m7 ^9 a. F- P' @& u4 e
//更新用户昵称
/ q$ B: y( i! l$ h: b$ n- E! y# b& s0 i $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ) `0 E8 B. e& }7 b+ F" A
if($nickname) { & S/ A2 u9 R- D& j; @/ e3 d- K
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 3 T5 ^/ `3 \; p% ~5 z8 _) u
if(!isset($cookietime)) {
7 d, ^7 J X2 O: v: \ $get_cookietime = param::get_cookie('cookietime'); 1 R7 U M8 e& ^8 X+ N. n4 N% ?8 n4 ?5 s
} ; T2 p1 \' B2 G! v5 o Q
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
8 N3 \8 R- U @3 | $cookietime = $_cookietime ? TIME + $_cookietime : 0;
- M# h4 M+ s1 j- ?) p/ y param::set_cookie('_nickname', $nickname, $cookietime);
. a8 Y) F6 G6 T% c }
# q3 ~9 I' h, X% j require_once CACHE_MODEL_PATH.'member_input.class.php'; n/ o5 f1 V7 N" P
require_once CACHE_MODEL_PATH.'member_update.class.php'; Q- u+ E6 w, B9 ^: h# O0 p
$member_input = new member_input($this->memberinfo['modelid']);
- Z* A5 L" f* e; z2 S $modelinfo = $member_input->get($_POST['info']); & I) d# W5 U T+ }' r
$this->db->set_model($this->memberinfo['modelid']); 9 o# ~! j/ ?* [* n- D
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 2 I+ k5 s; p$ o0 `! O: b! B; |. j
if(!empty($membermodelinfo)) {
8 ?: x8 H& g$ J $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); " I# n* X. [/ O7 ]' K
} else {
- ]0 W9 x/ e: [0 `/ ] $modelinfo['userid'] = $this->memberinfo['userid']; ( ^! E+ @( W E9 L" I6 O
$this->db->insert($modelinfo);
+ e' O+ r( }0 r, A$ _0 O } 0 N' u- ? Q+ u; y8 Z
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
) y" i8 G6 u5 D在\caches\caches_model\caches_data\ member_input.class.php 文件中:
# q f* b P( r6 O
7 r `8 l" @5 v5 D3 D
5 T2 e- s: ~7 _1 g
6 h6 d' D- l( _$ vfunction get($data) { . `7 z1 k* U: _' H/ `8 P) p
$this->data = $data = trim_script($data);
) g7 r# ?/ A5 k $model_cache = getcache('member_model', 'commons');
" v1 r, i. ?2 g( I, F/ i $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; . Q8 N8 A& @8 M T4 }8 g- n' y
$info = array();
4 J& `# U6 B% |8 n5 Q( { $debar_filed = array('catid','title','style','thumb','status','islink','description'); ( T3 _+ l& W2 z6 k. K7 |% |8 h6 A2 H
if(is_array($data)) {
( W. C2 u5 \( ^4 T/ {/ l B$ P' I foreach($data as $field=>$value) {
/ `5 O, w$ Q* P/ ? if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 7 F. j4 r+ q" c. Y! D; H# R' j
$name = $this->fields[$field]['name']; 2 J% |4 \2 u' Z. Q8 C4 `4 C: h
$minlength = $this->fields[$field]['minlength'];
+ A( N" M2 {( ~$ w* I) _, F $maxlength = $this->fields[$field]['maxlength'];
9 Y4 G1 m/ ]" F0 M) B $pattern = $this->fields[$field]['pattern'];
) V* k' i; Z1 k3 k0 S $errortips = $this->fields[$field]['errortips'];
( K: h0 C1 o3 @3 Z5 M- K# Q* i if(empty($errortips)) $errortips = "$name 不符合要求!";
) [: ~; X6 b2 S$ W( F2 r& C1 x $length = empty($value) ? 0 : strlen($value);
! z5 ~$ t; V. l% C2 m if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
. Z3 v0 x4 ]1 u- q( o2 V( ^) ^ if($maxlength && $length > $maxlength && !$isimport) {
( N2 ] L/ G5 ]. X4 I/ H showmessage("$name 不得超过 $maxlength 个字符!"); : `! g% w$ z* J6 E) [ d3 i% s
} else {
3 L8 o4 ~+ U* j4 @& M- J str_cut($value, $maxlength); 9 x) }7 o' m5 g1 ]
} 3 D- [3 ^& y9 H3 q
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
P) }6 c. `! a9 u( i if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
1 X! L) Q8 k3 S; W! x. U $func = $this->fields[$field]['formtype'];
) d, {! u0 H( u0 m- F+ I, Y if(method_exists($this, $func)) $value = $this->$func($field, $value); ( G7 I+ b5 T- G% o9 w5 K# l0 }9 s
$info[$field] = $value;
/ B1 r) i( ?) m# s3 @' s } 2 G+ ?* k$ d$ u3 Q' r
} 3 R6 B1 T: a1 V7 J& ]4 u
return $info;
. K' g: ~$ n8 X1 s/ A! V }
* M/ y1 H# X; {# U- qtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 U- ~ O# a$ _1 t! Y8 k
0 B7 _$ }9 o: `) {) R再到phpcms\modules\member\index.php 文件account_manage_info函数
3 Y* }+ Z- E4 S2 |+ M% @8 K" M8 }, v过了get()函数之后。
m: e( U- k2 c3 K9 e9 Q/ F9 O( F
$ I6 ]4 Q4 y* H& X- M2 f
5 ?; w1 K$ c% ~$modelinfo = $member_input->get($_POST['info']); + l1 W- N2 e% z, O- F
$this->db->set_model($this->memberinfo['modelid']);
* f, w8 F. ~- s $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); : W. s! x' I3 ]! J" i2 z+ u
if(!empty($membermodelinfo)) { 9 F- `% Q$ d; E
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
! e0 ^: ^# R+ p; i1 a# i5 | } else {
9 z( Z) {. c# ?* l1 h直接带入数据库,update函数我们跟进看看
, p8 I; q/ l7 r' Z R0 B W) k2 V5 t9 c; `
0 K. f8 _& ]0 n- c$ l3 `
public function update($data, $table, $where = '') {
& M% s0 t! R, j* p# _0 g' l if($table == '' or $where == '') { ' N6 W3 y3 o. H
return false; * B1 k9 Y/ |1 \9 W0 M
}
: v- k A8 @: ~0 l9 n: e $where = ' WHERE '.$where;
3 s. y5 e% N0 ^ $field = ''; ; u1 X( u4 |$ e4 U5 N, f
if(is_string($data) && $data != '') {
" y; ^- x3 K% g, @1 V- j5 N8 u: u $field = $data;
$ C! W2 s1 ^+ r* O* S: a% | } elseif (is_array($data) && count($data) > 0) { 3 E* E) H* q. H) @ e
$fields = array();
; d7 n" O' _# r# _" ~5 X+ z! R5 h foreach($data as $k=>$v) { 5 M& l2 x+ O3 b" o
switch (substr($v, 0, 2)) {
4 E h" O5 D/ k8 d$ R& ]; p8 t case '+=':
' v/ L7 L+ t8 ?0 J1 H% T $v = substr($v,2); ) X3 {' r' G* w. U
if (is_numeric($v)) { % y0 q7 S1 w1 N9 x
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 4 D4 D, J# V% V/ |& e/ e
} else {
3 p: r2 z" m5 v% G continue;
8 e) B: J# [) j- X3 b6 c } ! J0 f2 U( g1 \0 g" K$ ]
break; ( k1 z" R3 \& \5 R! Q; X7 \
case '-=':
! m" I/ H: y8 D# A& f $v = substr($v,2);
! {- Y1 K ?; T/ [ if (is_numeric($v)) {
/ h8 B) @* K" d( A. {% V $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); + p, |8 p8 s5 ^) }5 |2 d1 y
} else { - j2 K* X% Y( I; E2 r' {
continue; # J" j/ w9 m8 x7 y
}
# G* D5 w' b1 Y break; 2 `# Z5 E* i0 B
default: ; l& ^: ]' n+ o5 i9 A6 l
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
- b/ _5 x- |+ x2 s0 E) a } ! U; d+ L3 ?0 d- a; T! S; q- E
} 3 G/ Y/ k; w/ Q6 h! I
$field = implode(',', $fields); ! z' T+ b' g' a/ ?9 `: A- q3 a
} else {
: V# a6 Z6 G0 h% t" C+ G return false;
1 \2 s' [; h8 [* a }
3 _1 G4 K4 Y; Y2 d $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 5 l6 r5 p3 l* a+ j" n6 U
print_r($sql);
7 a( y) {( U5 z* I* v return $this->execute($sql); & s5 ?& T5 w% C' C3 Y
} 3 F$ \+ ^! v9 b2 ^% ]3 ~ C+ x- U
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。9 w8 l; Q7 D+ A/ a) H- w9 m
4 i2 p. @( }2 n) o, b攻击测试:% s5 X K7 X# ^! i5 ? t+ J
测试地址http://localhost* c' U* k: \& U9 o I1 v* U
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句- X- G4 K' N6 i! s) T2 |8 F' x! d
X, |% M, g5 y: [2 j2 {- |# g' s6 Y
# o, K& F) [ [4 }- y/ l
! x% G4 A$ N/ i. S" Y$ f+ M' Q5 e/ R; `: a: t
7 t. l" N7 k% O" D0 R$ Z+ C
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|