|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
$ L0 H* c$ F. g8 S7 {漏洞作者:skysheep
8 H1 r% Y8 w v! y0 B9 ]! V( }2 e分析作者:Seay
/ l# y- q3 W* A* R! I博客:http://www.cnseay.com/$ ~; k; H% s" H8 i
漏洞分析:
0 a3 o- ?" ^! e# |! f$ Q! s1 O; W 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 c* L C" `6 u' f; o' {
* y$ _- d" x2 C& `% }8 w) f
" e" Q" C1 a7 S
( H. o- M8 S) `# v2 T8 i5 }public function account_manage_info() {
! Y3 x$ n+ E4 ~3 W+ L2 ?4 S0 q8 X if(isset($_POST['dosubmit'])) { 6 B! J" t/ e: Y. S7 }) [
//更新用户昵称 9 ^' e3 F _7 k% C; X
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
9 E# I: ]7 c$ y% E$ e J q if($nickname) { " W7 c1 m F1 r4 C
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
! c% B8 P6 V9 n7 `# D3 d8 q* V if(!isset($cookietime)) {
! m4 d& O+ ^; r" G4 Z $get_cookietime = param::get_cookie('cookietime'); * W3 k$ |# g9 _4 v4 e |" i# ]8 Y5 `1 d
} ' A: @" O$ N! ^# x
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 5 y" L! G5 J7 _+ E/ J, s& w' ^( k
$cookietime = $_cookietime ? TIME + $_cookietime : 0; ( x! a" Z1 `1 W
param::set_cookie('_nickname', $nickname, $cookietime);
& e3 {% p6 {! _; j+ @ }
3 M3 Y g, [% v2 H- o require_once CACHE_MODEL_PATH.'member_input.class.php'; 8 X* u5 A, \7 t5 p3 T; `
require_once CACHE_MODEL_PATH.'member_update.class.php';
- {6 I Q) Y$ H% ~' o! o $member_input = new member_input($this->memberinfo['modelid']);
+ I/ o0 |9 k0 O H$ s2 F8 D! R" n, j' A $modelinfo = $member_input->get($_POST['info']);
9 u7 [- J) i2 b' g $this->db->set_model($this->memberinfo['modelid']); 5 u- r/ c, d: M
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 A8 _" O4 f) d0 i, o3 H if(!empty($membermodelinfo)) { 9 g ]1 f U0 N7 i+ z
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ; M- |$ e [( l Y$ T/ p* F$ W
} else { 7 M; \/ y- S e0 r: R
$modelinfo['userid'] = $this->memberinfo['userid'];
$ P5 H: }1 a7 r" q) p& b- S1 v $this->db->insert($modelinfo);
# I9 R6 P, R0 I; @5 ] } / w) R4 ^6 p$ o: ]/ ~
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,: ?* {( Z+ L1 i7 i
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
. s6 v1 Z! O- A( @3 x7 ^ z J" C6 C
$ C& ]3 `' z$ [' T
' W) r+ y3 m3 R% k& x; J
function get($data) { + s+ P- v a5 e( k4 D- [
$this->data = $data = trim_script($data); 1 A3 O* q0 ?( v( g* m, Q
$model_cache = getcache('member_model', 'commons'); ( f% X4 O8 d- I6 M2 }
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; s) i. r0 q3 i( r; b
$info = array(); 2 R. X) ~( H0 p1 ~7 Y
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 1 f1 K% L0 D3 y6 J6 L" p% f' o- ]
if(is_array($data)) { ) ]1 t) A4 N7 k6 g' u& H1 V
foreach($data as $field=>$value) { 2 q' m" X# ]4 t. f" V- C
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
, L/ G5 \* T$ s/ O- H $name = $this->fields[$field]['name'];
7 p, \# s9 |, a2 V7 k9 _ $minlength = $this->fields[$field]['minlength']; 5 v8 [8 \, U* h
$maxlength = $this->fields[$field]['maxlength'];
8 e/ n+ }' Q! H1 S $pattern = $this->fields[$field]['pattern']; 5 d7 I7 p, g+ \2 B% A. h J
$errortips = $this->fields[$field]['errortips']; 5 A+ {$ i1 }4 m, `- \0 f
if(empty($errortips)) $errortips = "$name 不符合要求!";
+ N/ C; T( w Y, B. Z0 n $length = empty($value) ? 0 : strlen($value);
# ^# `5 u* o) @0 {- y if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
9 c! g' V4 B& k. D- w if($maxlength && $length > $maxlength && !$isimport) { ) P% c3 Y! |7 E( N+ m5 c
showmessage("$name 不得超过 $maxlength 个字符!"); ) ~, U& ^7 |! r; w
} else { * H/ D4 K! z' x0 I# _1 m
str_cut($value, $maxlength); 3 X" k" H+ @( h& |* I
} - R1 S; l4 y" Z- H
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
2 N& P, D: P) S if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ! F/ v$ Z& @2 m$ {/ Z' H
$func = $this->fields[$field]['formtype']; % r( i' s# r1 Y( m& W
if(method_exists($this, $func)) $value = $this->$func($field, $value);
6 i1 s. l6 D1 N2 E3 F $info[$field] = $value;
5 r( w6 l- ~8 z6 F } % a- y7 L8 @5 Q( ?& v- ~9 t
}
2 o! L# U5 Q E: X return $info;
2 I5 K( W5 u0 R ~' n } $ X2 ?2 e; [# n6 p; i
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,2 w9 u+ {7 z% c- |: D
! k. s# N9 |4 S
再到phpcms\modules\member\index.php 文件account_manage_info函数$ \1 A" l* N% H% x% o6 f+ u
过了get()函数之后。
3 t7 e- u: o4 h. x% g$ ?
+ {: u3 x1 P# l5 I0 y; ^ ) G6 F0 W5 T1 T6 ]+ R6 u
$modelinfo = $member_input->get($_POST['info']);
: x9 d. y! X" ?7 b& D4 [7 @ $this->db->set_model($this->memberinfo['modelid']);
+ C" e/ w. j" m4 _$ ~2 G( K7 v& | $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ( M! W) a0 Z# V# H% q
if(!empty($membermodelinfo)) {
9 p8 U3 W* Y* _. [* I/ M, S5 m $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 3 Z. e J6 Y% M: X- b- V
} else { + K( o. U0 S( m9 C
直接带入数据库,update函数我们跟进看看
& Q7 g" y6 k, X6 @: N5 T% f# F5 p, |7 @3 e& E
5 o6 o, ^8 B: z
public function update($data, $table, $where = '') {
9 u9 h5 W5 y, Y if($table == '' or $where == '') { % [8 m0 Q* C/ G
return false;
: Z. g* n) T' L) b4 l/ t+ ]3 W } 7 n- b9 @4 S* Y: u! Y) p# y* y
$where = ' WHERE '.$where; 5 S2 E8 y* Q. k3 }
$field = ''; 8 e. R. b4 Y6 Q' _! U3 O* O
if(is_string($data) && $data != '') {
6 \7 x4 i Q- ?' M( i2 S; h $field = $data; 9 R. b% v: A9 C8 W g4 Z
} elseif (is_array($data) && count($data) > 0) { - ] v) [! |6 M5 L5 j3 F' y
$fields = array(); * l* v9 x6 X( q: y
foreach($data as $k=>$v) { " r7 ]/ \4 l$ Z
switch (substr($v, 0, 2)) {
+ J5 \6 F$ p7 T( j5 G! g case '+=':
, L+ Z h* \7 ]7 R# W. }. [# Q $v = substr($v,2); 2 X# L" {6 D: M, f- X' G& B
if (is_numeric($v)) { & X6 ?: t" l; ^! B! [
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); / G# f7 B) S( K+ C) p
} else { ; \1 r& n, ]. a- A. M- C! R& D; Q1 A
continue; 0 w6 b+ u' Q6 j( k
}
, P" d) m% h6 F% r6 |( R, g, t break; & i- d8 `" K( ^' N. l* w
case '-=':
7 f5 u; R: G" \. f $v = substr($v,2); : R) A9 U% X" ^% E, h! P" ^% L$ b! j
if (is_numeric($v)) {
: X6 G9 S* K1 \9 q $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
?% |6 p* _' s9 k1 A } else { , X A$ Z( G4 j3 D6 ]$ T, M
continue;
4 d% p; Y! G9 F/ A7 l } 7 B" p8 E- Y z
break;
5 a& Y; T( L. q! y% J/ E0 u/ x default: 3 n9 b( ]9 ^1 I
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
* F$ l, u2 p1 g9 M& P' I } 2 X& H, i2 F& v) h* P" A
} 5 Z& P5 e3 P$ s. _
$field = implode(',', $fields); ' j/ G9 f7 ~" `3 u! t! I7 B0 Y
} else { * y# X8 P! V* L! N
return false;
* `1 [0 z' {% G# s3 y" W }
6 d; J' ]+ Y/ S2 ] $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; * y# t5 t9 s; }" l) p* p* [1 @' H
print_r($sql);
. Q' ?6 M9 y: F% h: v4 ]5 i$ X; Z3 n return $this->execute($sql);
7 y3 m- V1 r( w( T } , o$ A, d* c, Q5 l; A
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。, Z$ E/ _0 s/ m2 B% k- t
3 } h8 P7 u# G' |+ ]攻击测试:
: z$ M' Z3 D! X, k0 B5 q测试地址http://localhost
6 G- q/ v# y; z { 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句7 j2 C# e1 i, j" `. s2 f( z* E
+ M5 h, x6 e5 v5 ?0 c* \ 3 J% n" j; O, D$ e( p9 s
/ R0 ^ r2 [6 Q& k/ G& j" l% [' h: @
/ q r' j" R- W6 s7 i6 f- g1 j3 J' j: _3 X0 D7 Q" W. {& B/ t
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|