|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
* }; \! |& I' l, V8 M+ [' X4 w) f漏洞作者:skysheep
- P0 Q1 a2 @0 a分析作者:Seay
4 L+ l+ x7 ^. |9 s博客:http://www.cnseay.com/$ ~5 p, F$ j! P+ |$ H5 G7 C
漏洞分析:
6 f/ Y4 @& S" P+ j6 e5 N7 J6 s 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。5 T& K8 A/ J+ c
* _/ ]2 q1 ]5 w6 `1 z6 m
2 W" a! v. r2 o& L) v( M
9 t3 p: R+ _+ o$ p1 Fpublic function account_manage_info() { # y' K2 j& F) Q3 s E; G4 |
if(isset($_POST['dosubmit'])) {
. w# v1 a- C5 i8 f9 r/ e //更新用户昵称
7 q. M5 q1 D# C' c' {' b P $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 5 G& s& o3 H+ r
if($nickname) { 5 u) b+ F' V! l: B0 R- N& d
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); $ }- \: z3 K, v- N! g, p* {
if(!isset($cookietime)) { % V9 V, O5 {' D0 P9 j* x
$get_cookietime = param::get_cookie('cookietime'); 1 \+ ^ T. u, \, X( t- T
} : {% L3 n4 X8 V5 _" p' \
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
: Y2 |# _( W. t. c$ o $cookietime = $_cookietime ? TIME + $_cookietime : 0;
5 @, M; b* H0 s param::set_cookie('_nickname', $nickname, $cookietime);
8 e* |/ Z" V% U) Z9 V } ' P1 M* V4 l% F2 ~4 K+ }; G
require_once CACHE_MODEL_PATH.'member_input.class.php';
( H: Y: g! H9 N0 Z8 ~ require_once CACHE_MODEL_PATH.'member_update.class.php'; : X- Y% V" k; l& m
$member_input = new member_input($this->memberinfo['modelid']); ' M# s# M+ [" g3 h8 [7 C8 Z
$modelinfo = $member_input->get($_POST['info']); % P0 V/ A; B9 G3 ^$ }
$this->db->set_model($this->memberinfo['modelid']);
8 z/ Z6 P& H( c0 N% i- I $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 u5 ?6 O k4 V
if(!empty($membermodelinfo)) {
( S* n( y6 s6 m; `: R; b* F5 T $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
5 b; m- `' ? J; P; A } else {
$ W9 J1 {# q8 N& i$ q( @: x, J $modelinfo['userid'] = $this->memberinfo['userid'];
|& T" ~5 [) L9 X9 X" d( }' j $this->db->insert($modelinfo); 1 C, ?+ ~' [, | F l, z, ]
}
$ l+ g3 E$ m) O2 x: X代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,; y/ l% |' X. D0 o
在\caches\caches_model\caches_data\ member_input.class.php 文件中:7 B- p8 m R9 m
/ U+ ~' W" m+ [; O
, B4 o7 g+ o h+ t# p, U & u6 O3 I& q& O: {
function get($data) {
- a# T" F* z. ~ $this->data = $data = trim_script($data); / C( T, N- a# Q8 C* F0 L9 q% V
$model_cache = getcache('member_model', 'commons'); * e7 w S' A. d) w& i( O# k6 _
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 0 D( @" r6 Z7 A6 _8 {! F+ t
$info = array(); + {' v- O! S: u& _& F! |5 H
$debar_filed = array('catid','title','style','thumb','status','islink','description'); v/ R0 n$ x8 F5 ~& o
if(is_array($data)) {
# a$ A9 {* [4 W9 `5 m foreach($data as $field=>$value) { - E+ i! @% j" `; j" G, D4 g
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
% x+ {5 I2 }% I( q1 } $name = $this->fields[$field]['name']; 8 E/ q1 U& b2 _/ X6 k
$minlength = $this->fields[$field]['minlength'];
1 w3 z+ J4 w0 V" p $maxlength = $this->fields[$field]['maxlength']; p2 R; ]3 ^1 J. z! z5 u+ n" g5 s
$pattern = $this->fields[$field]['pattern'];
. o9 a7 N# k+ C5 P& i. e! L/ A, n. y$ b $errortips = $this->fields[$field]['errortips']; ) B6 G" L( B# R
if(empty($errortips)) $errortips = "$name 不符合要求!";
( G; o5 c, B+ f, d+ T $length = empty($value) ? 0 : strlen($value);
. T8 G6 N4 j1 j' | if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); . l/ _! T8 n, o. H- _3 G; r! W4 V; t
if($maxlength && $length > $maxlength && !$isimport) {
q% P& k% g5 c8 z showmessage("$name 不得超过 $maxlength 个字符!"); 8 V; y" l7 _4 |
} else { - z' m: v7 Y; Y( [. p" h5 U; T
str_cut($value, $maxlength);
4 [( D1 d! p! v+ t# A8 W& a }
5 z8 J7 E b7 a* V- | if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); ! D6 @( t0 n) J- L* `2 s
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
8 P m. N9 \8 j$ |7 D6 E! E) c $func = $this->fields[$field]['formtype']; % r6 ^+ G" {2 I
if(method_exists($this, $func)) $value = $this->$func($field, $value);
# T4 q, b' [6 [) O% E- U $info[$field] = $value; 8 r: I) _0 s3 m" Z9 F
} ' @7 ]2 j. f$ q$ y8 o
}
) \9 L5 q. R& g return $info;
0 X! \1 R+ K2 X: o. D } - d" v$ s% Z- |. B# ^
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,, t( j. a/ S$ u' |5 ~( V; V4 j
8 w& y: u6 j3 J0 T; [ f4 L5 w
再到phpcms\modules\member\index.php 文件account_manage_info函数
/ | T5 T2 g! D0 Z9 [过了get()函数之后。8 ]4 r9 W5 s; P4 ~( z
8 E3 q4 W N6 H3 J, J
! P* W1 `9 Z( v( G1 f$modelinfo = $member_input->get($_POST['info']); + o$ l" w! f0 {% v+ Q8 c, ^% @ x( u: s
$this->db->set_model($this->memberinfo['modelid']); ) m6 h' i* p: J/ y. ]5 W
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
! [/ N4 Q& d' U+ }2 j: q Z; _ if(!empty($membermodelinfo)) { / K- {7 r, D$ u u; F6 T% T I9 s
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 m% D% I8 ]! S; s# J
} else {
; r. e8 i1 A' r) Z- w直接带入数据库,update函数我们跟进看看
; M, V: s+ U) I7 {! N0 \) M3 `# w4 j9 V1 |* Q4 Z
z( I2 i$ w3 w9 B) x
public function update($data, $table, $where = '') {
' g0 M! x: h9 v* |( e. k, U if($table == '' or $where == '') { 1 y* l8 b& n- ^& X' k9 N
return false;
; b$ S/ Y- A$ A0 V4 P }
6 v0 _4 t/ p/ ^5 o) o3 n! `4 f $where = ' WHERE '.$where; 6 l3 r& Y" U2 X7 K) o+ a5 s
$field = '';
" p3 g9 K$ ~+ U( | if(is_string($data) && $data != '') {
+ j/ ^6 Z& @: A8 }. s $field = $data;
0 }- p r( G7 a" j& A) q! [ } elseif (is_array($data) && count($data) > 0) {
% w- I* S T3 T0 K% _7 e$ x' p; L $fields = array();
4 w% O- w1 a/ e3 S4 U+ U3 }3 m1 F, D/ s foreach($data as $k=>$v) {
% l: B8 t# h8 q5 w switch (substr($v, 0, 2)) {
, a0 F: l: l& ? case '+=': & V6 r5 F0 C3 e0 w# O+ V+ z
$v = substr($v,2);
. h7 C# j: f' Z; F+ L6 V if (is_numeric($v)) { ' |1 v7 e$ p j. W/ r: C
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); ! c c. J" ]* t
} else { 0 p4 J( D$ Z: V, P6 Q" f
continue;
/ d8 F) @- S6 G } # k. u+ }$ u1 T; g: ?8 a' ^" E& t* G
break;
" J) w; w; F2 r case '-=': 1 p _$ b5 J0 a+ o6 `
$v = substr($v,2); ' \9 j* _" v& f3 a6 e6 B c
if (is_numeric($v)) { : o2 l* w* P6 W
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 0 J, i: h, r9 G2 V6 ^. Y
} else {
( k' E/ k3 i# i$ n% L continue;
6 g; t+ }% E: m$ I. l( y v } 3 U9 z- E- v/ K* L8 M7 E; Y
break;
$ d h# R/ T7 L default:
0 B7 M) M5 E" _4 s G1 Q $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
: Q" G$ G) O# m* L7 | }
' j- @* w/ p5 V; P } ) \% s. t% ^' H9 C m# E
$field = implode(',', $fields);
" h' a3 z! C5 \4 a/ q+ d7 i1 G/ x } else {
: d2 L9 k6 }' i$ [, v) a return false;
+ @: Y. o3 N2 o! ^6 n1 I0 o+ ~! s } , r; _3 m+ O( L, C$ F- q
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
" m1 l& o: M2 z5 z print_r($sql);
, z+ e; ^4 W0 k. p/ ~ return $this->execute($sql); ' y6 a7 }( ?! y5 g( A2 P; v
} / I: ~0 X( y+ y, }
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。1 F) P, F9 C9 j2 [- p
: r+ E; D% Q) c6 c9 S, u( V; t2 i攻击测试:- T# |2 q- `: [5 z. U& _
测试地址http://localhost
. E5 S5 l! b: f. m 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
: o' w: @! z* Y1 w& T# i/ |1 o/ b$ x) K4 j4 V0 B3 ~
; G' C: C U6 {! d6 h2 c5 w
. j9 x( S8 S( ]' H1 i
5 }* I. W$ B$ V" a0 Q& ]
6 o. P: x! X! Y' i% a
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|