|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
: y; e0 V1 ~9 j漏洞作者:skysheep7 L8 f1 L8 Y) J# g' c9 X
分析作者:Seay* q& n( ~& y2 ?& g
博客:http://www.cnseay.com/! l7 x; n6 F! _4 l0 O! Z
漏洞分析:
6 F) g- I7 X0 l* Y 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; h2 U. P2 Q2 i/ ^0 G. u
, O# N3 ^. ~/ ?5 l) r
9 ^0 b6 o5 n' B! Q7 H 3 G/ w# @: P$ f7 C( Q- v/ X( `
public function account_manage_info() {
/ c" n0 s: p! _0 B% g if(isset($_POST['dosubmit'])) {
( k8 E( p3 o2 [) ` //更新用户昵称 ( z; E+ |' H& J" Q0 Y; p' P( j
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ; [: Q/ }6 ~3 C: }, Z- \+ Y
if($nickname) { ' [8 b5 `0 k' X, s. ]
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 7 i' S- u5 U6 A3 B& \3 |
if(!isset($cookietime)) {
! G! Q7 R$ s3 n $get_cookietime = param::get_cookie('cookietime');
5 o$ W3 @6 s6 _: i ~+ R" J }
, f5 ]3 V$ s$ t2 L $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
" P5 y5 c/ j8 A: z$ z $cookietime = $_cookietime ? TIME + $_cookietime : 0; 3 ~. j* i4 |8 }# |1 L1 @
param::set_cookie('_nickname', $nickname, $cookietime);
8 s9 k V2 B W' j; _ } 9 y) k! Z7 Y3 |2 v) I
require_once CACHE_MODEL_PATH.'member_input.class.php';
X+ ]" l% n( H require_once CACHE_MODEL_PATH.'member_update.class.php'; ) z- t+ _3 d; `2 k( ?; c% Y
$member_input = new member_input($this->memberinfo['modelid']); * M+ N/ T3 f; l5 h
$modelinfo = $member_input->get($_POST['info']); 7 g$ ?4 ?; @6 w9 A: }
$this->db->set_model($this->memberinfo['modelid']);
$ p) \( w; o, `% E: |9 t $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); * k# Y5 f$ G8 m3 [- J" k
if(!empty($membermodelinfo)) { 0 S4 l2 I* h- P
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 q$ S! ~2 H' e" J: C0 ?- q
} else {
, y: `- |/ |% Z) J $modelinfo['userid'] = $this->memberinfo['userid'];
" y, T- Q2 |4 V4 y, U $this->db->insert($modelinfo);
1 g: n$ V( o R } 4 C# l* l) V# t& ~* Q) f9 r+ k
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
x* y, u# A; g6 q% e I在\caches\caches_model\caches_data\ member_input.class.php 文件中:
5 C- f% C, ?/ G1 [5 s$ `) ?+ P3 J% ]2 c* H/ X$ w+ w
" z2 q+ G: {! x/ Z- c$ T
) F# C% D1 W9 Z9 P& j! x' j) Ofunction get($data) {
2 x g1 G+ x% R2 q- |. d& B: | $this->data = $data = trim_script($data); + y. h" o% s$ d) M- Y, }4 k
$model_cache = getcache('member_model', 'commons');
0 D( Q4 z2 |+ H6 Y b1 ?; T $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 7 A% V# N% _1 ^5 r
$info = array(); ( G3 q3 ?7 Q) ~' y+ |7 r2 u# s
$debar_filed = array('catid','title','style','thumb','status','islink','description');
3 Z/ A6 U$ o, ]8 F9 w6 ^2 l5 ? if(is_array($data)) {
8 b$ ~$ n! @! u9 ]% e9 W1 } foreach($data as $field=>$value) {
3 F- \5 j* A# V" v if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
$ `0 M% ~2 y/ N $name = $this->fields[$field]['name'];
6 V6 u# Q2 |8 e $minlength = $this->fields[$field]['minlength'];
% z) R; i& L* s- P- h4 h $maxlength = $this->fields[$field]['maxlength'];
6 y3 C f0 M/ ?1 Q$ v $pattern = $this->fields[$field]['pattern']; " c5 m0 j# j/ }5 U" N( z
$errortips = $this->fields[$field]['errortips'];
+ x! {4 P2 `- e. c7 R- o c if(empty($errortips)) $errortips = "$name 不符合要求!";
5 D m: |. ?5 v n7 d' A) q8 X" E $length = empty($value) ? 0 : strlen($value); 7 u# O! S4 L z6 t; W: P" D
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
5 F% [. Q' U# N ^- n/ h) X if($maxlength && $length > $maxlength && !$isimport) {
! T/ Z$ ]) c7 Z showmessage("$name 不得超过 $maxlength 个字符!");
6 w+ o2 B0 ?8 _. M$ J } else {
% h/ V8 Q0 @, t4 c, t str_cut($value, $maxlength);
" k$ `% d) s' \. J$ a8 n } . J7 b5 \; |6 a% m8 z* h$ s% Q
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 0 G9 _3 e) K! V) ]
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 6 p' E2 ^( A) ]# }4 A* p8 Z- |
$func = $this->fields[$field]['formtype'];
3 _9 Y2 S% h. a# T+ H if(method_exists($this, $func)) $value = $this->$func($field, $value); 0 M& } T/ C" } v) b
$info[$field] = $value;
/ ?& x8 Q; }4 G/ g* b1 H0 ? } " u$ o; O u" ^& T1 m! P5 a
} 6 @% |: C1 [: L% D' {. h; H
return $info; : y0 b5 z( {$ x! ?
} / n. H4 u2 n2 q6 [: v# ^( M0 E) |7 S6 a
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
/ S) a9 c# {* E- m0 x0 C! K, H; o6 a& F. ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
: w1 ~- F! F$ H过了get()函数之后。
. _1 } [0 x" f# {5 y% a7 W1 }, T, ?8 b7 Z; v* d3 r! A. Y$ o
) G$ C9 a0 g y5 Z6 |8 O$ H+ T
$modelinfo = $member_input->get($_POST['info']);
8 j& o2 P4 j5 j9 `; ^5 T6 G" | $this->db->set_model($this->memberinfo['modelid']); - [ _6 ]& J2 n& K/ Z
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 \8 o: ^/ r P4 b% g if(!empty($membermodelinfo)) {
; b4 u/ \$ [! e. ^ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
& n$ q5 F: A4 x4 d" J. O } else { * ^5 j/ [2 |) S) q9 B3 d2 N- A
直接带入数据库,update函数我们跟进看看" f" p/ ]. t" s0 R
0 L2 f. D, {0 g- Z+ B
7 s) D6 b; j. u' z$ Y" b
public function update($data, $table, $where = '') {
; R" z7 R% [; G" s; j if($table == '' or $where == '') {
4 M- M$ W8 Z2 Y m$ u; o6 q return false;
+ ^- ]+ X u j' b3 F4 s9 M }
' w1 F$ b& T, w. x $where = ' WHERE '.$where;
, q7 g9 I7 q* M% ^0 m; H $field = ''; & B& M, ~% m$ y& o/ a
if(is_string($data) && $data != '') {
! O" ]) i2 @4 D) k2 o: B $field = $data; # r6 |) z* Z1 G5 l1 n) B; l
} elseif (is_array($data) && count($data) > 0) {
' Z6 F+ ?: H! g $fields = array();
5 H9 W( g0 ?: x7 G _' b5 h6 E foreach($data as $k=>$v) { 9 V9 b4 v9 r+ h% A7 ~! a" Q
switch (substr($v, 0, 2)) {
) }( q6 }0 _2 u9 M S case '+=':
/ ~. P* R8 z D: F" T $v = substr($v,2); : ?9 k/ ]! i- M/ } l! w$ L' e- I
if (is_numeric($v)) { % F8 K0 G, r9 V, u9 d
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); # k! N$ ~3 F1 q" o6 c9 e
} else { # g5 N5 |/ T# q- G
continue;
' w! v s# X2 K! M+ y }
/ X& V1 x8 }, K( g) Q+ | break;
0 t) t8 [3 F+ ]1 G5 ~ case '-=': " }7 K4 D3 d, y& ^+ O# C
$v = substr($v,2); * D0 i' o0 K) x+ o" H8 ~; J. K
if (is_numeric($v)) { + x( W6 [$ B0 A- |9 @/ X! ?
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
6 J% L4 I& H; B } else { 6 v+ N3 v& L( A1 a! e/ W: ?
continue;
5 p/ T* n8 v& k2 ?2 { }
/ n( p0 x) r* X2 N; Y break; $ H, i+ a, l/ x) j3 H( |
default: % P. V& J7 o) o( |
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
# d$ w) m5 h# W$ w } 7 I( N; b) G1 w: W
}
* w/ U* H% F' V4 p* P' S $field = implode(',', $fields);
0 n% [* J0 B, T } else { $ o. F/ u Y5 g% \9 r% t+ C' P3 e
return false; ; U2 n! p! F. D$ E1 ^- h
}
' j8 i4 L% ]+ A1 O5 B* g9 H/ n $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; / o9 B. S" w2 \* b$ x
print_r($sql);
1 t' Z2 L. o5 o- P) U# K return $this->execute($sql);
' m1 P1 M; g) C }
- n/ R' F# p4 P$ K6 q从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
. R) {; e/ y# B& o$ F) l! z1 v5 V
攻击测试:
- g6 b7 C5 t) x `0 [测试地址http://localhost
5 q" e# T8 c' A4 a2 g6 F2 K$ A: y6 Q* Z 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句0 X& [; `/ e1 e* f. W
* G8 u- S) h$ a7 T* o( Q0 Z* V. L
# K9 ]! }% _( n
4 }! }5 F8 M& j
1 k5 d0 m/ T1 x$ Q% @
; V/ }6 @) A# C
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|