|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告) E7 ]% F% W" w9 x: C
漏洞作者:skysheep
7 q* U% _' U& Q3 b! z& Q分析作者:Seay
+ H2 U2 O4 |' r+ v( a- @) G博客:http://www.cnseay.com/
/ K2 _$ @4 f- G# {4 i$ _漏洞分析:
- B4 c' H; C* Y2 m/ J* b9 \ 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。0 w0 ?7 M: S! `$ Z e
( {- h5 x9 T( r8 B
4 I# L; h; v4 N& M) X! s, F) T # @- Q6 U, C8 M# Q8 K: f3 k' r
public function account_manage_info() {
/ a8 V7 h0 b3 X6 I" F0 X4 m if(isset($_POST['dosubmit'])) {
6 D+ c0 b6 h: A2 [2 s0 c9 @9 @ //更新用户昵称 " A9 N* ^5 b4 N+ T! f
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ' A, ]4 c+ W4 G- f4 N
if($nickname) { 9 }: ~7 ` Q0 o) M+ _2 }! _
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ' g7 {5 C3 F; P
if(!isset($cookietime)) { 8 i( j9 o$ t2 L) x+ Y; p* S
$get_cookietime = param::get_cookie('cookietime'); 5 i. _5 Q4 E4 | X* f
}
, i5 n! ?. S) @- T# b7 q $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ! {$ M" |7 p& w3 n
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
5 j: f2 j L. u H0 {' N param::set_cookie('_nickname', $nickname, $cookietime);
0 R, U; {4 ^, ^) X } ! V* L7 j8 g2 f+ j+ c
require_once CACHE_MODEL_PATH.'member_input.class.php'; . ~" B4 ]1 g0 ^9 D5 j/ W
require_once CACHE_MODEL_PATH.'member_update.class.php';
s( a) Z Y6 \ $member_input = new member_input($this->memberinfo['modelid']); ' E% ~) P4 ?0 D) ^5 \
$modelinfo = $member_input->get($_POST['info']); : z! |$ A0 A# n
$this->db->set_model($this->memberinfo['modelid']);
1 X& B3 U+ X: l% x' u2 l- {5 ? $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
$ P4 ]/ ]$ T7 K3 E7 u" z if(!empty($membermodelinfo)) {
5 H; a: F2 d& c+ U/ ^- V+ p7 \) j* [# d $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
, e0 W4 J7 K7 U3 A9 R7 |1 r3 B } else { 5 ~. l2 X) r4 J4 S1 m
$modelinfo['userid'] = $this->memberinfo['userid'];
+ c$ C* k% O, K" P/ ~/ X- ~5 |" }; `9 o $this->db->insert($modelinfo);
$ N4 E' r3 u: l- B1 H/ g) |5 G! X5 I } - i t0 F- T% c* u) i
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
* `, U3 v! ]7 N! N L在\caches\caches_model\caches_data\ member_input.class.php 文件中:& Y8 r8 K+ V) |# E- Q
5 n+ k# H! b i7 w! {
* @ H. U( l* S' K i
1 Q9 o& x4 R$ v/ vfunction get($data) {
7 a" b, p/ O% E# o6 ~! C4 s6 N( d $this->data = $data = trim_script($data); $ h6 i) M& _$ j9 `' Q
$model_cache = getcache('member_model', 'commons'); g5 h% {( o* X1 o% h
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
8 F. h3 |) S) e/ g0 t) A M $info = array(); 1 U# _* r8 a& y) n
$debar_filed = array('catid','title','style','thumb','status','islink','description'); Q, B6 _ f1 w5 `3 |. ?
if(is_array($data)) {
6 s" k V1 q. v& F foreach($data as $field=>$value) { 2 w5 }$ ^! t1 U) `+ w% O: j. O1 O
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; / n+ o: D( F* m, P* s0 i
$name = $this->fields[$field]['name']; ) I. P J6 V) {6 {% g7 s# f
$minlength = $this->fields[$field]['minlength']; " @% r' K0 i, ]0 c6 z( p
$maxlength = $this->fields[$field]['maxlength'];
* u, X# Q) T4 x2 H- P0 _ $pattern = $this->fields[$field]['pattern'];
6 Z: T: V" t8 a. ^% E9 a* D0 S $errortips = $this->fields[$field]['errortips']; 0 a5 y/ f2 Q/ }" g& G! T
if(empty($errortips)) $errortips = "$name 不符合要求!"; + i S1 G9 c( f3 x& Z$ p( V
$length = empty($value) ? 0 : strlen($value); 0 l: ^$ N+ L0 ~7 M% q4 j
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
5 W' u7 Z$ ~" Z. N& U& P if($maxlength && $length > $maxlength && !$isimport) {
6 m! L S0 D g5 ^/ {" D$ o2 q showmessage("$name 不得超过 $maxlength 个字符!"); 3 J$ E1 r/ K; \0 p" K( p
} else { % K) }' n9 S" {
str_cut($value, $maxlength);
0 c* ?- p" s: c( x }
8 u8 M& G+ p/ g9 I# T( ^; w if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); # p: k1 z/ ~+ x4 _ Y' n6 r
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
' B- {; B/ V0 [8 J/ M# S& T- q $func = $this->fields[$field]['formtype'];
* q* d) m1 B4 e/ N" x if(method_exists($this, $func)) $value = $this->$func($field, $value);
: ^' G# a) b0 C8 I7 h0 ^ $info[$field] = $value;
9 Y" r: T8 K' ~5 Q1 s* w } 6 p R" q5 U/ J9 f2 s
}
/ ^/ i, o e4 X6 ]: V return $info;
& k7 x: [) u" F J7 ^/ P5 U }
& p$ ?7 n* h6 q" T L- ctrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
- u" |7 l2 N, A% G$ F% G8 D0 M# ]' W& b7 B0 m
再到phpcms\modules\member\index.php 文件account_manage_info函数
6 n+ S. Y7 m( l; i% [过了get()函数之后。
; R6 I" O% G, P% L# C3 w/ v7 {. t) \& |
) ?" n: z0 H9 m2 `
$modelinfo = $member_input->get($_POST['info']);
" K8 d+ J4 q8 Y$ l$ F $this->db->set_model($this->memberinfo['modelid']);
; G+ I* G9 p S$ c7 H0 c* Q $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); # |0 P3 b8 l, @% `/ M! @
if(!empty($membermodelinfo)) {
8 \+ K) b Q( ] | $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); # B; h8 e3 x2 n' C s9 I
} else {
7 v2 n; r$ b+ X/ h直接带入数据库,update函数我们跟进看看( K. {( Z7 v7 K% i* i
2 F9 X, f7 d3 L
1 m% @9 y8 a6 i8 ~6 z4 C: V! C9 \public function update($data, $table, $where = '') { 9 B) j! K% _9 m1 O$ e
if($table == '' or $where == '') { % e- W6 F" m1 P: G+ q
return false;
: a: ]" S0 d4 d. u0 ^ }
. q7 F8 e, @! \9 s4 ^( ` $where = ' WHERE '.$where;
. F. M6 Y6 P) n $field = ''; * f8 m4 ?2 B: }; s
if(is_string($data) && $data != '') {
$ N+ F# e- b1 z' h% s: U1 l $field = $data; + d& D3 e1 B E; T7 s7 T2 E6 j8 P8 r
} elseif (is_array($data) && count($data) > 0) {
# c; o2 w9 ?% U $fields = array(); $ z2 k0 d- ?9 F2 V8 q1 x
foreach($data as $k=>$v) {
- a; z; @: p1 Q4 ?3 c. ~ switch (substr($v, 0, 2)) {
/ l' X7 [; A2 ` case '+=':
- W j% v7 s& f* j $v = substr($v,2);
1 F1 g2 [$ B# w9 m, Y/ r1 C' G if (is_numeric($v)) { 1 v, \' m5 \" d
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 C Q; S" w& a& t7 B( a } else {
& }5 e1 c: ^6 L& T Q1 V& P continue; " j. b8 O @ X; U q& V- b
}
5 R! K9 k* b7 q; u/ h. A9 I& G break;
5 i m1 g, ~1 h* X$ N case '-=':
: _+ n4 n/ [$ p( w& i e $v = substr($v,2); 9 g4 V0 }) b- v- G, H3 ^, P
if (is_numeric($v)) {
9 D" W z9 E. ]! I7 L $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
3 M# Q& H4 z) w, t } else {
0 w5 U4 W2 B* V/ m. B continue;
7 j w$ W. q, B2 m; G }
7 |4 e. K' v2 ~ t break; 4 Y* D V( j, l. B" v
default: 8 v6 `0 d8 Y8 |* o% K
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); + r6 o9 n0 K4 q3 h K+ ?
}
' M2 @0 N/ K6 q6 l y } % V9 m& M9 F6 v: I ]
$field = implode(',', $fields); ' P% R; N2 f) E( h; ?4 W
} else { 0 Q* C) H: A) W y$ T7 i
return false;
- ]# j, ]& z; B4 _' Y) p' i1 Z- S }
+ ~2 V2 f' H. ] $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
7 t2 c) g. G; V3 ` m5 ~$ e/ a print_r($sql);
_0 r0 p/ m# P: O/ }. Q return $this->execute($sql);
. `' h: V' l5 E: J1 I, K2 X } : e6 [2 U, g+ {8 y5 r9 X& x
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, E$ c' K E$ ~! J# ^7 T `, W& J
1 y# t8 V( ~ ?3 {( R- T+ |攻击测试:
3 j, F3 D- p( k测试地址http://localhost) y) R# f; x4 T0 n' ?% j
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句& O7 U- ]2 K" Z
1 t1 K1 Q0 P8 q
7 `: o, p/ R9 ?5 H5 M
6 R5 w* s+ n' [" k( W
# l- J5 ?& g7 v% f( H7 ^" I
( o( ~8 ~. Y: s
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|