|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
) ?9 j# Z8 l) B5 Y$ `漏洞作者:skysheep- J3 p; O% S) {
分析作者:Seay
: R% N/ O& r3 p6 C3 C* H博客:http://www.cnseay.com/
9 f. e: ^0 P Y/ t3 i% ?漏洞分析:
- }; V+ W1 g7 ?* w; [0 c# `8 I 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) t0 w; `, m% a8 p& r( u6 u) ]& N: h K, k, p6 W# E
- K6 k7 O K. c" Q. J0 [; X
) p6 a+ _, s* Gpublic function account_manage_info() {
% `, c+ h; x5 J) ]4 Z' l5 }% \ if(isset($_POST['dosubmit'])) {
+ T' n/ z# r) Z: J# b //更新用户昵称 6 `) K' @: d% X
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; + P1 {9 K0 l! U* v) x
if($nickname) {
$ F& p; M# e" o $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
( x# S9 `1 h" w# Z4 R# R if(!isset($cookietime)) { & M2 Z3 s: K4 N* t" {% @+ i
$get_cookietime = param::get_cookie('cookietime'); , e2 d6 t9 q2 r# \' _/ _, _7 C# T
}
$ h3 r8 [/ b M* S% s, ^ $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
& W2 C( n, n, |& m $cookietime = $_cookietime ? TIME + $_cookietime : 0; 0 L" u- X, O) {5 U
param::set_cookie('_nickname', $nickname, $cookietime);
9 ^$ \+ W$ P* c }
, p1 ^/ {. N% r% P0 H; r) n+ b require_once CACHE_MODEL_PATH.'member_input.class.php'; ! D! [5 J) w5 s% |
require_once CACHE_MODEL_PATH.'member_update.class.php';
2 }# Q# {7 l% M $member_input = new member_input($this->memberinfo['modelid']); 5 E2 A8 y" S6 k" x8 C4 v
$modelinfo = $member_input->get($_POST['info']);
$ `9 L9 y' p" i $this->db->set_model($this->memberinfo['modelid']);
+ r1 c2 J0 I- W( c1 I. b+ ~ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
' B+ z5 R' H, a- s if(!empty($membermodelinfo)) {
/ B8 N8 x) S% k1 o+ {2 Q5 l( J7 N% y $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ) w4 s7 n; q' D" D
} else {
, _7 S0 @; |0 o. d5 D O' O $modelinfo['userid'] = $this->memberinfo['userid'];
' w& f4 t4 f. ]. l* ?7 |" x8 v1 G $this->db->insert($modelinfo);
% \% o- m4 M7 g }
2 j# E4 v( N3 l8 P) m代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,. T) f9 M/ j+ l3 [
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
X+ F* X- k+ O4 J% y6 w5 c3 o8 ]- b9 s8 t" R& Y3 ], N
' R* @4 K- c- G; {+ Q
- w2 x' h7 \$ e+ Q, Y
function get($data) { # [% t& t( r5 u
$this->data = $data = trim_script($data);
0 h; O' S, O7 R4 D; U& O1 E $model_cache = getcache('member_model', 'commons'); * {4 y! j& k! y/ `! G. L G* ^
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 1 q1 ^ {4 k: R8 f- W# R; |
$info = array();
5 g+ e# _/ |; l $debar_filed = array('catid','title','style','thumb','status','islink','description'); " r+ A# g V- Q. q- e
if(is_array($data)) {
j( ~% k; ?6 Y. q, K foreach($data as $field=>$value) { % Y3 Y' t5 e# ]" i
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ! O4 d3 {' }' v8 L
$name = $this->fields[$field]['name']; 5 I ]; l* r$ a9 Q+ X% `
$minlength = $this->fields[$field]['minlength']; ( S) |: V& [- J- B; c E
$maxlength = $this->fields[$field]['maxlength'];
" ~# Z. S- R! X& G+ s $pattern = $this->fields[$field]['pattern'];
: \- I+ C7 r! V $errortips = $this->fields[$field]['errortips']; - P4 X7 b: y' D8 Z
if(empty($errortips)) $errortips = "$name 不符合要求!";
9 P H/ I8 G, @ $length = empty($value) ? 0 : strlen($value); : E3 t3 G% v% u4 [2 ?/ V# D
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
8 K2 [4 ]$ b; F) @ if($maxlength && $length > $maxlength && !$isimport) {
( v+ T: K' X: E. m: ~+ M7 v! U! [. @ showmessage("$name 不得超过 $maxlength 个字符!"); 5 o: Z+ ]! w9 A
} else {
8 G# h+ }+ Q% n1 A str_cut($value, $maxlength); 1 K8 o. {; D8 l/ \/ b
}
% ]; Q5 c5 ?8 |/ }$ L" f7 Q6 r if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
2 e F; P2 E! J- B' Z" { if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); $ M. J k; P1 I; Q7 m
$func = $this->fields[$field]['formtype'];
; K$ ~$ L, J6 Z if(method_exists($this, $func)) $value = $this->$func($field, $value);
9 r( ?# y* x" a A $info[$field] = $value; * H/ n2 x/ e% B' X
}
x, s4 T) z1 Y# u }
+ H3 O" G9 l' \$ q' @. Z return $info; ( r1 I# H- C; r* P
} ; u) |7 d1 `' N
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: Y0 R/ ]! [, ?& Q
: s9 C4 ?; p3 t; M+ G. \2 N V再到phpcms\modules\member\index.php 文件account_manage_info函数
6 d* H, \4 }5 r1 v1 q* C& p过了get()函数之后。
' D' {3 w3 x1 ]/ d% A6 m9 c' |
) r/ I$ x8 M8 j2 y! ]' I9 C : l" L! K9 L% N; F! L
$modelinfo = $member_input->get($_POST['info']); * T, d- {: R% C6 ` ^2 G
$this->db->set_model($this->memberinfo['modelid']);
; ?7 U5 g. v2 d8 S $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, o/ U2 ~9 x7 B if(!empty($membermodelinfo)) {
1 l/ }+ O$ q! f: p* E/ [7 \ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 G, Y3 r( @0 P0 p! E6 L
} else { ! w/ h0 A; S N8 @
直接带入数据库,update函数我们跟进看看
5 @: Z9 V3 b0 \, Y4 w, a& W6 H8 a9 b0 z
7 q: c( W9 r/ |' q
public function update($data, $table, $where = '') {
, ~- [& Q) l2 D$ |, J5 a& O if($table == '' or $where == '') { ) `, G1 d/ B; w; y, b+ Z
return false;
" e5 y5 V1 l* |! v1 p) C" h4 X2 h } ) b; w/ m7 w: r' b( {
$where = ' WHERE '.$where;
) }: m: x3 \- O$ H! l0 v $field = '';
$ ~% Z" }1 y$ m* Z0 G1 U if(is_string($data) && $data != '') { 6 j$ e% b$ j8 X
$field = $data;
1 w. _4 {4 M( [9 r& j$ q } elseif (is_array($data) && count($data) > 0) {
# b( e l/ [8 S! C- N$ f1 k5 X4 @ $fields = array();
' k/ x2 m- d6 T- E: b9 z) m foreach($data as $k=>$v) {
! x/ f9 Q, \0 @: u) j0 L0 V switch (substr($v, 0, 2)) {
4 Y" H4 j: U6 I( ?+ F1 A case '+=': 7 d$ v& D2 z7 Y! m- Z& M) W
$v = substr($v,2); ( H; N9 J- _3 m' }$ Y! E
if (is_numeric($v)) { . |8 j) P3 g5 h- n
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 6 K( T+ V* m9 N- ?+ w2 W" Q% d
} else {
2 }7 t' S, I& B* z continue;
) D; I8 @9 i8 A& E0 N" e8 U6 G } ' g4 p5 E" a. K
break;
6 K9 Z7 {0 x) u5 G/ Z case '-=':
6 x" `4 @1 ~1 ?8 N! C: x9 m/ e0 i $v = substr($v,2); ; Q: z+ J9 U+ A& P
if (is_numeric($v)) {
* h6 ^) J, `' s3 Z $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ' B& t* Y" e" \- F
} else {
3 L; e" h; S+ c8 z" R2 y" u continue;
9 s W# {2 f6 \/ b- Y2 q1 N6 E } : {% x& a) B9 a" |) {
break; , d) |( o4 {8 P9 ~
default:
& M4 z+ J4 p' f0 _; B7 ` ~# W0 q $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
* E9 Z3 o. ?: Z. b } L6 H9 {2 z' D7 R+ C+ W2 C& [
} + t4 w! A2 r' \& `: V+ A
$field = implode(',', $fields);
* |# W4 M0 @/ `- @% X) D } else { 5 B/ Y! X) D/ ^" d' ?! Z& Q. P
return false;
; O3 W. l: Z' L' n" w/ J) A% M9 z }
+ n% j f8 p+ l, _/ n. H, A $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
8 ~; E/ \6 L$ \ print_r($sql);
4 p; Y9 A: L2 u, w9 _6 Y return $this->execute($sql); : k9 q0 D, \7 U8 y8 |
} 9 j m- l+ `# w( E( R8 |
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。4 P5 B* q0 l! y' \8 l8 p) A0 J1 h! W
; J% ]* ]8 I3 d6 z+ {攻击测试:
+ A& _1 o, n" u! G( y5 x测试地址http://localhost( N* b, y9 a: r% [0 a( Q' i6 J
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! S% G. Y4 a6 b* \6 }
% R2 B, d- u8 Y+ c ) P Y. }6 b# Y$ k# y
) v- {! T& G! u7 {1 i1 G K0 R8 H0 m# W9 O* E, V
" i9 ~0 n! _* D4 ^, c0 O/ f+ m' K |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|