|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
5 `. e3 g( T, \+ S漏洞作者:skysheep
. H3 f: O9 [9 i6 \分析作者:Seay2 c6 Q) p$ x- F# y1 f) Q1 X) v
博客:http://www.cnseay.com/
+ A C! u0 Z6 x漏洞分析:
+ Y7 o, }8 F6 K q; ~* N/ N1 ^ 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
! k# B9 u8 x E# M9 k2 _9 x3 p9 [
" N! {( P6 h: @ & a, G; ^% D" A; y" X; R) c2 j
public function account_manage_info() { ! D- u2 j! F) F
if(isset($_POST['dosubmit'])) { 1 `& w) z; |) O/ R
//更新用户昵称
) [8 R$ t- R5 R( S U2 a3 v/ H $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 9 \: J5 P9 z' b
if($nickname) {
\* x) A+ x! V8 ^8 _$ _+ Q& W& t $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
0 j1 W' q7 d) L if(!isset($cookietime)) { * Y% N9 Z/ b7 c& N
$get_cookietime = param::get_cookie('cookietime');
2 P$ \0 w7 t3 G l: ~% |( H } 1 U5 K) C6 G+ k/ `
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
3 g- w e) l& e6 K; G+ ^( u $cookietime = $_cookietime ? TIME + $_cookietime : 0; , X0 u2 q( ~8 V# ?9 @$ L
param::set_cookie('_nickname', $nickname, $cookietime);
2 W9 J' n0 C( ~; n+ f/ X8 ] } ' G$ Y' y/ p. l( f
require_once CACHE_MODEL_PATH.'member_input.class.php'; 7 |* {' u, z" \; }
require_once CACHE_MODEL_PATH.'member_update.class.php';
9 q, g9 ^; u7 d4 u- G5 [ $member_input = new member_input($this->memberinfo['modelid']);
. q! |( r! e9 W1 D( V" H w. I, D $modelinfo = $member_input->get($_POST['info']); & d% L3 M5 B2 Q! N8 A
$this->db->set_model($this->memberinfo['modelid']);
! b8 Y; ]' n/ a2 N $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
, d. s8 ]" [/ C/ {' ^ if(!empty($membermodelinfo)) {
6 v9 `8 \# ]. |* h/ U $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ! ?+ d* i2 I( v* Z* F
} else {
) e9 G; c: u _( ~* J $modelinfo['userid'] = $this->memberinfo['userid']; 0 w+ ` r5 J9 z6 E
$this->db->insert($modelinfo);
} d: f. ?0 J1 ^" k/ | }
- C1 V) g# y" Y4 D3 u- t' |代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,% f4 _$ j4 x# j
在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 [0 Q/ d& E/ J. F: Y9 Q0 T% A
% t. S5 ^4 m( A
" ?0 _% I4 \3 Y- B$ O
" F7 {' v3 k) N; M1 Y. Y
function get($data) { 1 e) t' t. w$ R- D- L
$this->data = $data = trim_script($data); ! X) D. g5 t- z, w' F0 I
$model_cache = getcache('member_model', 'commons'); ( q+ f0 o7 s6 x# Z
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 7 Q& d; m$ y. ?$ ^
$info = array();
0 s# y q/ H I. x7 q $debar_filed = array('catid','title','style','thumb','status','islink','description'); , p1 S) j H+ |- k* v
if(is_array($data)) { / m6 H$ O% P/ Z+ E6 D
foreach($data as $field=>$value) {
* H2 f3 ~6 v9 E8 | if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 6 {/ F* a) x3 a! X3 K7 {
$name = $this->fields[$field]['name']; ( P, D8 C- D' Z% I& F
$minlength = $this->fields[$field]['minlength'];
! K* _; R% e6 M1 N9 O8 F $maxlength = $this->fields[$field]['maxlength'];
) ]* N3 Z7 ~8 Q3 s5 Y1 A& { $pattern = $this->fields[$field]['pattern']; 7 @+ E, p8 L: L" X8 W
$errortips = $this->fields[$field]['errortips']; . R P2 a7 b% x7 z
if(empty($errortips)) $errortips = "$name 不符合要求!";
! O3 ] |. S' w $length = empty($value) ? 0 : strlen($value); , z0 v+ t$ [5 o( X
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 1 }' J# e$ |$ }+ M3 b8 `6 M$ R
if($maxlength && $length > $maxlength && !$isimport) {
* {- D5 D' g2 R ]. Y- N showmessage("$name 不得超过 $maxlength 个字符!");
9 V/ h7 i8 _* [# K } else {
- b7 V& r# _) X' ^) M' `6 n: { str_cut($value, $maxlength); " g0 N2 F7 ~% q3 k2 ]
} ; w/ O& ~ ^- g# X- z
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
+ ~- t- [' u3 q2 u if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 4 a+ G0 N6 i- O+ v( G( T
$func = $this->fields[$field]['formtype'];
: G& g1 W' E8 A2 [" t ~ if(method_exists($this, $func)) $value = $this->$func($field, $value);
) T4 Q, e$ ` ?9 f% n $info[$field] = $value; + E3 ?' I8 V4 D& s- q
} 0 t# F* |0 b6 ]. ^7 d
} 8 R$ \' ?4 U6 I
return $info;
+ F: f" w0 E/ D5 \ } ' e3 P$ _" Y6 A6 o0 ` |6 E6 f
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ _$ [3 z/ [: `$ \) q( [
. S5 V8 G6 N1 G8 x1 o
再到phpcms\modules\member\index.php 文件account_manage_info函数2 X' A* \5 I) |( t6 }" _9 D* q
过了get()函数之后。$ y' n% M: W3 u3 j3 Q7 Y- f: r- Q2 J
, \( @/ T& B: p K* t( I- w1 @
C0 B9 G) {: o) L3 E% B4 X
$modelinfo = $member_input->get($_POST['info']); $ i$ |6 n( G- R9 ]
$this->db->set_model($this->memberinfo['modelid']); : e, r+ ^% |, ^9 e) E7 T8 V
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); - i/ w1 ?6 \) k
if(!empty($membermodelinfo)) {
' Z3 O% ^2 r. U2 ~% [ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
0 ^& M" \8 ` g: {/ |! H% J4 P+ D } else {
: [- x9 B: K- R/ x/ f3 A直接带入数据库,update函数我们跟进看看
1 i) _% k& @ _+ K, F6 b# ?( B" |) h/ H- ^3 a- z3 S
+ m8 I" ~1 }4 C3 \) o, V/ s
public function update($data, $table, $where = '') { " C" }; \6 D3 a" i7 H5 e
if($table == '' or $where == '') {
2 p3 C2 H5 g" Q& B# X) G2 { return false;
, a* Y6 a- I V9 q4 m0 b }
5 H& k3 O- U. q$ j" g $where = ' WHERE '.$where; ! Q( R& N N; t# ]- U' O
$field = '';
g g0 V# t9 i6 e* @) { if(is_string($data) && $data != '') {
( y+ X) u) q4 E3 y* R% ] $field = $data; ) ~: X( Y1 \- F# F+ r
} elseif (is_array($data) && count($data) > 0) {
, n% e. S% x: _9 L4 ^( o $fields = array(); ; X- ?! F* z( w3 \4 ?9 m4 n: R% n
foreach($data as $k=>$v) {
" }: R( P+ ]: v! m switch (substr($v, 0, 2)) {
3 i3 L1 o2 w3 V4 t case '+=':
$ p6 i9 K; d1 N $v = substr($v,2);
0 ^: e- d8 S9 {6 U+ o' L+ u if (is_numeric($v)) { 0 r4 M7 K) R$ N4 c3 g+ U
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
+ t" n9 v/ x2 R7 F3 d4 g } else {
6 N& O/ l6 H/ J4 d: U" f3 i continue;
) Q! s3 _+ q7 \2 p$ K% q# s. \; \ }
# e1 f4 w9 ^' c: `6 q L# Q1 Z break;
( [1 B, d8 ]) s3 q case '-=': 5 B& i) q2 I3 Q, I3 X
$v = substr($v,2); + o. s# a$ g/ Y' g2 Z
if (is_numeric($v)) { * V" @/ `) Z, T" R9 \' R3 P
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
0 F7 V' r( X8 v( Q0 k } else {
, [) M2 x, ^$ C& e( L continue;
0 G! X* z) ^; @4 `/ p } 3 q5 u( I5 [4 k" b- U' O1 R
break;
" A9 y0 ?3 `- N; e default: ( B% R8 J% D* R2 m8 h, s
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
8 d* k3 R. V5 |5 n5 ` } 7 I: L5 C$ c+ ~8 c1 q+ W
} . o$ ~% m' v7 n6 j
$field = implode(',', $fields);
4 m l5 w5 y8 a! F3 G } else {
# I4 k+ ]9 g0 F& {4 N3 Q return false; ) Q1 _$ v4 i- Q1 q! O3 F8 Z5 V1 ~
}
4 u: e' I. @2 R+ x0 j' ^ $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; . g- a W' ^* _/ Q5 m
print_r($sql);
{4 v: _3 D4 |4 b return $this->execute($sql);
5 _+ n1 E( D+ h T. q; a! l$ x3 W } 9 z& B* o% f0 S" }( L
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。) B6 K* q1 `! K; {
$ b9 D0 L) G( H6 t! \
攻击测试:
2 _6 |: E: G/ K! } I4 K. Q$ p, F( e测试地址http://localhost" p2 V' r; }- a! y* m! H
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句9 u M7 @9 z' O C5 H
9 T3 Z# w3 o& Q$ w7 Q
+ H8 ?0 R7 x* U7 c
+ E# V7 K7 i) x/ A' ^5 c- c0 j! d$ D9 R4 I8 C
0 I. a6 k' V: w1 k7 h- C
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|