|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告2 _! j. b ?( L
漏洞作者:skysheep3 o3 }% u6 M* ~! H* k# z
分析作者:Seay2 m0 ~' r2 Z' ^3 J
博客:http://www.cnseay.com/1 y! e! K0 h0 p1 a9 c! t
漏洞分析:' a! B5 R7 z# G1 s% P6 \
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。7 H1 g& U3 o8 [
/ x# V1 J: G) B# ^, f
( Y8 E/ U1 v. U7 u& |4 A * P( z5 }5 b ?5 I# l0 n- o
public function account_manage_info() { W/ i6 K) f4 b1 p C, f
if(isset($_POST['dosubmit'])) {
! w5 b A8 X# o. p- C //更新用户昵称
, _8 w! h( B5 } s $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
7 s, v T2 R5 G) ~ if($nickname) { 0 ? H2 y3 K/ E4 W
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); / e1 ?6 M8 r. z% e- S6 a, }5 F
if(!isset($cookietime)) { : j3 I* A3 H5 [$ D
$get_cookietime = param::get_cookie('cookietime');
, ~1 ^8 f2 y* e! `5 H+ d } 7 X# v# j6 u3 f# G, L
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); + h: m% a* K3 R: J ]5 I9 V
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
7 n( f+ Y- z" ?8 E* l' E9 V param::set_cookie('_nickname', $nickname, $cookietime); 2 b* ^) F; v- A3 C9 _
} ' S* h+ r5 d% i3 O/ L
require_once CACHE_MODEL_PATH.'member_input.class.php'; 6 O# \# Q, G9 K" t9 u
require_once CACHE_MODEL_PATH.'member_update.class.php';
- h0 C' ~: d9 b5 C9 j4 U $member_input = new member_input($this->memberinfo['modelid']);
5 D: L# G6 @2 z% J# }9 w $modelinfo = $member_input->get($_POST['info']);
m+ K _, w# o: j* U T $this->db->set_model($this->memberinfo['modelid']); 0 U0 [" U" p9 N" L
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ! `8 f5 `" F! k Q- q. V+ G7 `
if(!empty($membermodelinfo)) { ( i, w7 g. R2 _2 M* d5 e! H
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
3 @5 r; S! x2 \7 r } else { 5 A' _5 l1 E1 S% M
$modelinfo['userid'] = $this->memberinfo['userid']; : T2 R+ k `) e) l/ H6 M1 C
$this->db->insert($modelinfo); , X' k& [4 \- I( Y- ?5 d8 p6 ?
}
' v" ~9 ]3 t- K5 a; l& Y代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,4 S" v5 J6 X1 r) ^
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& B5 o( R9 v5 l. z
5 O( i3 w4 Z: O! h
! U' N8 C* |, z# I2 _9 b : D: P& O6 Q8 u, M! @- g$ x6 P
function get($data) {
& m. H; a" E- v4 Y6 e$ _ $this->data = $data = trim_script($data); . M6 C1 f b- s9 s
$model_cache = getcache('member_model', 'commons');
- `3 o( U' J" X; O) x* s $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
7 X5 P8 g6 m3 L' X1 w( S $info = array(); " M4 G/ M& N7 z7 v
$debar_filed = array('catid','title','style','thumb','status','islink','description'); - E0 n) V$ r) _5 i# U/ [! h, h9 E- b8 b
if(is_array($data)) {
( W2 C6 r! u. J foreach($data as $field=>$value) { - v, J9 d/ [& W7 e; O. d
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
: K( W0 Z$ P$ K3 z5 S& } $name = $this->fields[$field]['name']; ) D; C% C1 O! R0 i9 i% ?1 d
$minlength = $this->fields[$field]['minlength'];
- N" r% e' X6 A( y$ } $maxlength = $this->fields[$field]['maxlength']; 2 a% d! b8 r# f, d9 P
$pattern = $this->fields[$field]['pattern']; ) k9 p% C* l% }3 a: x6 s" f3 r- J% M
$errortips = $this->fields[$field]['errortips']; 6 `1 o: t2 Z' G6 p, u. W
if(empty($errortips)) $errortips = "$name 不符合要求!"; ' r2 {, r" m# O8 L' }3 _
$length = empty($value) ? 0 : strlen($value); 5 q7 ]+ }: }' t/ F) Y' A
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
- v- B& R0 p& d. n9 y if($maxlength && $length > $maxlength && !$isimport) { . |9 O B x- A# b9 a
showmessage("$name 不得超过 $maxlength 个字符!"); 6 M. d5 j* j* j: g- ^
} else {
; v1 q% ^. J7 v1 r9 Z: t" d" D str_cut($value, $maxlength);
# S. k9 \/ G+ y, z }
" m A9 ~7 }9 u) g2 H if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 5 N& M0 y4 m* V
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
! t/ G( c' R9 E, s. O& \) u. H $func = $this->fields[$field]['formtype'];
4 E9 N7 ]- |" \- H; n$ `9 Y$ r% m if(method_exists($this, $func)) $value = $this->$func($field, $value);
% P6 Z1 \! N+ p) ^& } $info[$field] = $value;
1 w7 ~ T7 t) J } / Y1 T2 J& z8 F) K I4 Q
}
: l% `8 t9 s. \$ _0 L2 } \$ r return $info;
/ @& F! ?1 N) M+ \$ [ } 4 N8 p- X+ _) q- N7 w" |) a# {
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
$ D/ Q* n: _7 B4 \+ l T& U* L0 U1 c! g; d, s+ {( T4 S6 L: ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
$ O2 a: T u9 w$ V/ Z过了get()函数之后。2 T% ]; b9 O9 N
! i5 H9 T H4 W" V- v
. y) a+ R9 g" W( z1 r8 [. l! X$modelinfo = $member_input->get($_POST['info']); # _$ r( X/ x& ?1 |+ w
$this->db->set_model($this->memberinfo['modelid']); 2 \/ U, Q& B( K# W# }5 s
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); U# u: r( J3 C# C
if(!empty($membermodelinfo)) {
& y1 r( `; `& s, f" o/ c $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ! h7 a" `6 s& I/ d
} else { + C4 b N9 U: l7 ^
直接带入数据库,update函数我们跟进看看- i8 d8 W0 ]# I' D7 n1 u1 e% S
- @. S/ b5 I, N0 `' V
0 y" b( ]/ B2 m; C/ D3 [& F7 ?7 spublic function update($data, $table, $where = '') { 5 x' s/ `& s' A- J& ?
if($table == '' or $where == '') {
5 e- [% u& T: ]' I: ] return false; ' v/ ?% i1 q# T0 q3 O! |6 C! F
} 7 a+ z) X: ^9 w
$where = ' WHERE '.$where;
. G6 |2 R. g8 ~; j$ s3 K! h8 }7 h $field = ''; ( N2 q/ C* [4 S: K' l4 {) I) Z1 ]
if(is_string($data) && $data != '') {
+ R; Z! f) Z* M Z H4 X' g $field = $data;
& b) {, ?2 Q) P) Q+ m4 f/ I } elseif (is_array($data) && count($data) > 0) { 8 ~$ O; E8 {3 {* O9 h' e
$fields = array(); J" s7 R# s" _* E% a
foreach($data as $k=>$v) {
' g& ^! G8 G& d O ] switch (substr($v, 0, 2)) {
1 D% r6 H, ~) t case '+=': ! V- y0 g3 j( p* T* W; E
$v = substr($v,2); 7 H( V/ k% c4 ]* f3 H: g3 d
if (is_numeric($v)) { U1 u$ d/ v0 f/ \. R3 K
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
; @7 X3 M9 c X } else { ; g" i1 W; j) M! d" X5 b: r
continue; " n4 I/ ]) v9 F' _# z0 q2 Z
}
! i% S: ]) \5 w2 }6 G: ?5 x break;
; ^ D" p7 Y8 e; t1 ]# I5 T9 L case '-=':
4 w# s8 u$ c7 w# p, r $v = substr($v,2); ! [# e7 d! q K" n1 y
if (is_numeric($v)) { 9 r' Z% C/ u7 J0 t* W9 A, f
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 9 x" D: R3 m0 z
} else {
* g2 S$ _1 |0 F' T& t ? continue; 2 h0 E+ ~ N! @
} 9 e6 o7 {9 U2 y9 w/ z! t' d+ ?, c
break; $ Z# j( O. q* v
default: : a2 {- g6 _3 M* u7 z/ Z9 C3 O. ?
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
' c3 a/ Z/ U& H- c* E4 g }
) H f& s: f: L2 h; \# I }
1 _, g5 P' A1 i5 y7 C1 f $field = implode(',', $fields); 9 m: k4 B& n; h$ q
} else { 1 t" J6 T1 {# n
return false; " I4 @6 d5 F6 |7 J
} 9 r# o7 H( O# S+ c3 d3 q
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; * y+ p4 U) o c7 X& \8 }
print_r($sql); 9 O/ ^* G* n4 j6 R& {5 W$ x$ E1 A ?! C
return $this->execute($sql);
5 P) `# q5 i: S* Z4 ^) ] }
5 T! m$ o9 T% \1 y% G$ ?从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。+ D0 u6 g7 o. T. T# g6 ~
% v3 h0 v& V$ X
攻击测试:7 `: ]; `& J1 m& s* ~9 k* u
测试地址http://localhost
/ p0 L4 P( n$ M, c. y- D 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
0 O8 m5 }; D+ j2 m- D- q: q3 N
; ]3 D0 J5 y9 o s( t * m, W' |$ L: `9 m8 ?
( Y+ Z0 s1 y" m6 @* |0 z& X3 G
+ W `/ w* w3 h7 @
" Y/ G) d9 e$ h |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|