|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告% m0 p2 D" M0 _
漏洞作者:skysheep3 s7 `0 Z% e% T, q* n+ H& t) A
分析作者:Seay
% s u3 Q2 ?7 @6 O3 M& C, k8 c博客:http://www.cnseay.com/
* o4 O: n" m/ x. Q- [" j- H# f漏洞分析:! s+ e0 M7 i- b) F, E3 N
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
0 }1 A* _' _/ T, d! Q! [ J' r4 F1 I# ? _
9 X8 [$ z0 c- j0 F( ], H : M, m9 j8 g% V& b5 ?
public function account_manage_info() { 7 I5 s b* Z7 S( F6 i; t9 g. Y8 J
if(isset($_POST['dosubmit'])) { $ M0 ]# }! Y% }4 ^* I& v" u# c* t# `: x
//更新用户昵称
6 r* I9 U! i1 ? $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
2 p4 M q! c/ C- `2 r8 H( ?2 q+ i if($nickname) { & A# B% k6 ` Y1 U
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 1 {" n0 k5 |0 `; Q
if(!isset($cookietime)) { # U$ X! _8 U. k3 |0 [
$get_cookietime = param::get_cookie('cookietime');
2 w2 N2 Y, e, U ], _' R& W ? } " a; ]3 S2 I5 H& p4 w1 E) C
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ; o% `6 ^+ c7 Q8 _: f/ o
$cookietime = $_cookietime ? TIME + $_cookietime : 0; ! ~& y7 L; P# W$ @& l/ ?
param::set_cookie('_nickname', $nickname, $cookietime); ! v6 N4 J& [* Q; M( F! U
}
) L; ?- o7 l9 p8 H6 t* l require_once CACHE_MODEL_PATH.'member_input.class.php';
! V: d* ^: f$ k; ~) c* K$ @; H- Z require_once CACHE_MODEL_PATH.'member_update.class.php';
% X' _8 b9 L' i7 X/ K' K8 l+ b+ z $member_input = new member_input($this->memberinfo['modelid']); # m1 N: V1 e3 B5 _% Y
$modelinfo = $member_input->get($_POST['info']);
# t+ ^2 A' a& b& E3 } K* R $this->db->set_model($this->memberinfo['modelid']);
) [. p, c* ]# M" t $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 d( U" v5 o3 P: G if(!empty($membermodelinfo)) { 2 s+ s: T. Z) ?5 B! y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ( d! _. f8 s" {7 k
} else {
5 C% N5 A; Y1 F* ^ $modelinfo['userid'] = $this->memberinfo['userid'];
" H+ d+ G, y" Z, Y4 j7 |' e$ C( O $this->db->insert($modelinfo); 1 \$ H4 L& |. D2 W) |) {3 B4 F( p7 y
} & J4 x; b, A3 `7 ~. q0 w
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,5 K; @ N6 [) P" H% Q8 w# X
在\caches\caches_model\caches_data\ member_input.class.php 文件中:' ]& j1 T! x, W& | L# A
3 g: p1 h- T m& W' F4 q1 G
T- ~* f% W: p, M$ z' l7 `
" V% i+ w. o0 v- Ufunction get($data) {
' Z! D) r" B9 X+ [9 \7 v" T $this->data = $data = trim_script($data);
" z2 [5 \. O# A/ D $model_cache = getcache('member_model', 'commons'); ) K& K4 v. G0 h3 Z/ y
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
0 X% ^: p9 K7 F' X: @" S $info = array();
+ H" |& g0 W6 c5 R9 O $debar_filed = array('catid','title','style','thumb','status','islink','description'); ( A& K) Z( C' w9 `& e
if(is_array($data)) { ' J. K' Q9 V$ ^
foreach($data as $field=>$value) { + w+ J0 M; n7 _' L# q* S
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
9 x: B7 l2 J/ z& ?' J' \: [ $name = $this->fields[$field]['name']; * L: P/ F) h L) X; d& Q8 y
$minlength = $this->fields[$field]['minlength']; , ]1 F# }. K2 A+ K r, c( \
$maxlength = $this->fields[$field]['maxlength'];
6 g O n% R$ m8 c $pattern = $this->fields[$field]['pattern'];
- d. g3 v6 U+ {) o, ? $errortips = $this->fields[$field]['errortips']; $ C9 a) h/ a' d* [9 C% u% d! Q
if(empty($errortips)) $errortips = "$name 不符合要求!";
0 T5 Z" n- J! t3 n/ u $length = empty($value) ? 0 : strlen($value); 0 x% O3 s- f) B% \( f
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
/ u P$ R8 H8 m4 v: H) P0 l if($maxlength && $length > $maxlength && !$isimport) {
5 C0 E* H/ m* T$ H! T! j2 V; z showmessage("$name 不得超过 $maxlength 个字符!"); " T' w! V4 S" j& i8 F
} else { ) k4 m6 T( B; n e
str_cut($value, $maxlength); 4 G% l8 B; t% ~
}
& C+ C/ }( F q" @- c if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ) C9 A) C1 O% w1 X" S
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 2 p4 S5 \! C9 G7 r
$func = $this->fields[$field]['formtype']; 1 G$ _6 B2 O: \7 B% S1 m- i% g
if(method_exists($this, $func)) $value = $this->$func($field, $value); 5 W' h& A0 g# y+ P
$info[$field] = $value;
# _- h1 Q3 z' H }
" `; U/ X3 E3 t5 Q2 P' F }
/ ?1 e9 I/ o, M2 K1 n return $info; T" r& n% C, X% f3 O9 N5 f s
}
* A+ |4 _, ^, f/ itrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,7 M& z) e, Z" v
0 \# `; h' W6 P再到phpcms\modules\member\index.php 文件account_manage_info函数& r1 P2 O3 H. N h* D* L: O
过了get()函数之后。
/ M9 j! }; v1 j8 u z$ f) d7 u+ Y) x: d# X% f) Z
) j6 A) ]0 ]. e' c3 D$modelinfo = $member_input->get($_POST['info']);
5 X$ L Y8 ?3 s+ x $this->db->set_model($this->memberinfo['modelid']);
2 V5 o- p, B, W$ M9 {2 U $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); R9 p: r; \5 L I
if(!empty($membermodelinfo)) { ' E1 `; S* W% m2 s0 ]
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 7 t. _. n* B. w& ]4 v7 u
} else { 1 d) Z* Y' u3 W( Q! ]6 P, }
直接带入数据库,update函数我们跟进看看
& r+ `/ K$ M0 s9 k+ j1 t
W- O5 s4 }) x ~+ r! h* F
0 M9 Q; o' T, j$ @1 Q Gpublic function update($data, $table, $where = '') {
0 R: L, O; }5 R+ H8 T+ c5 d- q N if($table == '' or $where == '') { 9 u# J( S) v) U1 @7 ^6 L8 h
return false; 2 f9 I: R; a' D- X$ j
} # S a0 [* U" [5 `
$where = ' WHERE '.$where; 4 b3 |% [# [& s8 B4 O( B- c8 Z% ^) C& R
$field = '';
4 S2 {3 n7 e: \ if(is_string($data) && $data != '') {
: `' N* Q1 ^* H0 K+ K; t, V4 j $field = $data; : y5 Z' K$ E4 H% M
} elseif (is_array($data) && count($data) > 0) {
% M3 k, d1 N3 m/ `& J" s* I4 G $fields = array(); - l8 R0 f6 ]( o+ ^( a6 p9 e6 A
foreach($data as $k=>$v) {
. I0 U& a. K* ~ switch (substr($v, 0, 2)) {
: G( r' K( e* I4 V: a6 S; @ case '+=':
' A8 S4 }1 Q4 W) e4 ~$ l4 z $v = substr($v,2); # r, Q' ?, y) {' v4 ^/ U. S
if (is_numeric($v)) {
* P2 e1 b/ j: g; c $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 7 J4 n' |( Z$ V! i
} else {
. U9 d1 V0 s7 t& u9 \ continue;
& ?4 Q* Q8 I/ T! d4 ~ } # G# s2 E" s! e! e$ g- W' B5 p' }1 }
break; : L# r! y' h( X- C, ?
case '-=':
" k, m4 V; m( w: @ q M: |+ ? $v = substr($v,2);
6 p! q5 [( V7 Y+ Y$ P3 V. a1 r. R' ? if (is_numeric($v)) {
$ V7 ]2 Z+ s. v8 E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 9 U% K7 F7 K+ n' b3 Y7 V h& E
} else { 8 r) r: Y0 x# D4 c) w
continue;
+ D* a$ @) n- S( J }
# K& [3 f. \1 s+ [/ n( k- t break;
: P% ~' @3 F9 v1 @ default:
& |/ T% q. ^' [9 N $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); % o3 j6 ^; }! h$ L( i/ ]$ z1 `
} ( H/ g% j+ F4 r) [8 N
} 3 t+ Z4 L$ E# K; r w2 r. u, I/ {3 |
$field = implode(',', $fields);
& Q' B" h& q2 b9 G1 t: L } else {
) \( f; ~) S* d7 [ return false;
! m. R) N1 b: `% P5 }2 s- j4 ?+ ^! | }
& n& H3 X7 I. L) G2 k, U $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
, o2 q5 A1 B% R$ w; w3 E print_r($sql); 0 n% n; {! w, w- S' u& N
return $this->execute($sql);
: Q0 H9 R! I5 Z/ G8 }0 u0 K" K }
7 W: b8 l. q* c+ L. i. b从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。1 q* P& [; F( c; Q5 c+ k, I' }
" N; M5 X3 H& e, U( }7 o8 L攻击测试:- ~# f2 ^& a& i% t, W5 ~, U! m
测试地址http://localhost
6 |) w8 I D9 G2 M& W X 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
% s+ m: A# U/ v7 r2 x4 Y5 H" M6 I, ?& y8 z9 d; u6 j. X
" x* o, Q7 B- T# [0 a- P9 M2 i* m
' k- @7 n z( o- P: G: k" }3 t/ \6 ~/ x. ~% i/ F- U
; Y) \( Q5 x, _4 w
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|