中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
+ g% L- d' F5 {3 u1 `' {: J
漏洞作者:skysheep
1 z* S* K8 Z* ^! s) h
分析作者:Seay
0 ]& x" F. S( X
博客:
http://www.cnseay.com/
; ]1 z/ w% k3 q
漏洞分析:
3 h, P3 X; d& E3 F# y/ q
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
3 ?% g: D2 m7 g3 r4 [9 E
7 h! }& X4 |) x
8 V4 }( m! N0 H: `! H0 @
# I% L1 _/ L$ b0 l" J
public function account_manage_info() {
) {! ]$ k L8 N8 N9 I
if(isset($_POST['dosubmit'])) {
; X6 N C8 m$ F0 t2 i8 F
//更新用户昵称
0 O) O- R7 P9 T/ R
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
4 G9 V3 _- j0 E. Y& T/ o
if($nickname) {
; X+ }0 u, ^- l8 Q2 r4 h N3 }. W
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
4 Q4 [2 t, f9 o3 ^+ P! I
if(!isset($cookietime)) {
, L; p& k$ n. N- E) }+ j# |
$get_cookietime = param::get_cookie('cookietime');
. K8 i/ s( j4 c x" C
}
* Z" M) i* @1 j
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
% I ~( K! p3 [$ C# [3 N: o8 C
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
7 Y1 v! U: u/ P/ M
param::set_cookie('_nickname', $nickname, $cookietime);
2 V( N4 T' k# Q' p6 J
}
) N% Y( [. I5 f9 a
require_once CACHE_MODEL_PATH.'member_input.class.php';
+ `6 V, z+ N- v& @
require_once CACHE_MODEL_PATH.'member_update.class.php';
- R5 b8 Q& H6 R. N6 v. N
$member_input = new member_input($this->memberinfo['modelid']);
) b" f3 ]2 p7 K
$modelinfo = $member_input->get($_POST['info']);
9 g1 w% y+ D3 V
$this->db->set_model($this->memberinfo['modelid']);
" ?% V$ R+ V! P6 [1 a: W
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
- t. |, T9 W: N5 t: F2 y
if(!empty($membermodelinfo)) {
2 c7 m& w6 c0 U1 K$ U }/ }# a" h! Y7 ^
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
* _4 x" K! O% g1 m+ p w2 B# F1 Z6 q
} else {
6 n" h7 r9 j; [: w0 M
$modelinfo['userid'] = $this->memberinfo['userid'];
4 F, N" h0 E' {
$this->db->insert($modelinfo);
* o6 p9 b% U8 G& w
}
, D8 |' q4 ^5 v3 I- J4 O
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
5 n1 l3 Q2 I7 D( Q* e8 z* n
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
0 t& O3 A7 T3 M4 U
3 |( L& o( [% Y* V4 H
' y& i3 ^' F* ~8 |8 u0 t8 t
! y; E4 f0 U" ]5 q. ]$ d0 }
function get($data) {
$ d2 e; ^2 l5 y/ }
$this->data = $data = trim_script($data);
9 b6 P, N: N" g& e2 @" F
$model_cache = getcache('member_model', 'commons');
" F- O0 ]2 K* ~6 K& Q* ~
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
% S+ w5 V; E! q
$info = array();
& S- B9 N; e8 s! ]: [
$debar_filed = array('catid','title','style','thumb','status','islink','description');
" a& [4 w2 t+ F6 k6 q( s
if(is_array($data)) {
0 [/ m. t1 W# _' ?
foreach($data as $field=>$value) {
* l# Z: v0 A& I' t- J7 K8 k2 n% o
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
8 y$ \) g: x! f
$name = $this->fields[$field]['name'];
# V* [- ~" e. V2 s8 t6 o/ T# K
$minlength = $this->fields[$field]['minlength'];
3 X5 M& H: J$ n
$maxlength = $this->fields[$field]['maxlength'];
+ [+ K4 {4 z, Z/ t
$pattern = $this->fields[$field]['pattern'];
" `/ d5 W6 w. O0 `3 f4 B" x2 ^
$errortips = $this->fields[$field]['errortips'];
8 g- f4 B- O/ A2 P$ K/ c* ?
if(empty($errortips)) $errortips = "$name 不符合要求!";
* h5 f6 ]* h; m
$length = empty($value) ? 0 : strlen($value);
3 K# c: g3 B. o5 Q
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
, M# e1 P) @: x5 \
if($maxlength && $length > $maxlength && !$isimport) {
, n( u9 u$ F1 P0 F" }. J& l
showmessage("$name 不得超过 $maxlength 个字符!");
" A# f& V- P' {: Q0 p2 h
} else {
3 H; C/ a6 R5 b% {0 o ?
str_cut($value, $maxlength);
( B0 e" Z& h R% S
}
2 @2 z. H0 B' _* Z/ W- G1 e
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
# V1 _$ I5 F1 C' Y8 u" j9 U/ K" d
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
2 P5 y% y- S8 i) w1 ?
$func = $this->fields[$field]['formtype'];
! |. {, R6 z, R6 l+ K/ d
if(method_exists($this, $func)) $value = $this->$func($field, $value);
) m; u9 P3 \! X/ z$ C, [6 l
$info[$field] = $value;
* t- v; j& c; s8 Y% g
}
/ j5 d9 b) `% g/ B! o
}
c: C \( O7 J1 I: M0 C
return $info;
0 ]4 u7 ~+ X! O$ ` u' r, z' l/ H
}
# j) Q8 X0 }2 p/ Q
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
3 E: |6 J* `$ `- A) o0 h# f) J) X" y
; [& U! a0 o% q$ l/ e. ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
" g9 A9 F- C- `% R* W
过了get()函数之后。
6 c9 [+ [5 S" e
( o/ Y/ { N1 L" Z' G
% h. e. Z, v5 x' {0 M
$modelinfo = $member_input->get($_POST['info']);
7 n5 f6 Z5 P1 E7 p' S" o$ w
$this->db->set_model($this->memberinfo['modelid']);
4 M! r) t1 H1 b# {8 N2 i, l. _& B
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
! Y' J* \) j6 ~1 L8 a
if(!empty($membermodelinfo)) {
2 `3 f- q& q% u- r
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
) U) |# ]+ { z
} else {
( r2 h7 w( Z: P: \5 k0 r- A
直接带入数据库,update函数我们跟进看看
% r% E7 B8 P) Q8 e; [8 r: g
/ f( L6 s, R# U5 {8 T; E
: l9 A- E8 H( i3 I" [% z
public function update($data, $table, $where = '') {
5 [ J3 M- c. s9 [( L8 ]& K1 ^: q
if($table == '' or $where == '') {
, E# v5 ?& e/ t$ h8 k
return false;
. G+ q- Z8 U$ r7 q" }8 C
}
7 b y( P, r7 }; Q" [* l
$where = ' WHERE '.$where;
# Q& }( b$ v$ T3 e$ j# _
$field = '';
# W! t( j3 O" M& q
if(is_string($data) && $data != '') {
: ~! h' |6 C; w3 I$ @5 ?' r& s+ A
$field = $data;
1 y6 [! w, M; y9 B
} elseif (is_array($data) && count($data) > 0) {
4 N% {0 c. H0 j. k* q
$fields = array();
8 M/ }1 o1 E& j& @% j9 s3 t
foreach($data as $k=>$v) {
& s) I& W# b7 @8 O6 |
switch (substr($v, 0, 2)) {
4 ~+ {) |* M4 s% j
case '+=':
/ ]* [' N- o' Y7 |7 @8 M
$v = substr($v,2);
- ?5 |: q) \5 }% q1 [
if (is_numeric($v)) {
" Q: L$ v Y, ]. I
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
6 f- o- C2 z% e9 H
} else {
. i# D8 E7 ^: U; K, I
continue;
& p2 }6 t5 C3 D( y% E3 [; G
}
" Q X& {5 I& S' G
break;
3 H8 w! H3 v |; }5 G% e
case '-=':
7 [+ B2 X6 i3 E: ]: [8 K6 H: O
$v = substr($v,2);
5 u5 T; |+ I. d, o% x
if (is_numeric($v)) {
0 h) n( G! z8 V
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
$ b5 e; q {. F: n# ~
} else {
( w2 _# a! O' m" A& g
continue;
- D$ _. @) b9 m& h% T6 [9 @
}
! D2 G1 O' W2 o5 t* q, b- g
break;
* r7 K' r* `5 [) c8 Y$ q% t, E3 C
default:
2 L/ {9 O6 V# }7 u- r! s
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
' {! i8 t$ ^3 l. H( A, x5 Q
}
& ^1 p7 B+ \1 c
}
4 c( ^3 _ A; v6 v9 C& P! J
$field = implode(',', $fields);
) \6 T" {# @: L2 Q: Q
} else {
- A0 c: X, Z4 d( a6 T" m. h
return false;
. b, A) ] E, [0 d% h
}
0 R& v' G, s- @% x6 ]0 [, F
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
' g5 P6 e& W8 D: ^8 e7 j
print_r($sql);
- T0 Q/ Y( V; i. \0 D
return $this->execute($sql);
" `! }, k7 f, G2 \
}
. S* j7 D* C5 s0 j0 s+ @+ v, U
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 T6 G8 w* m$ ]! X* ^) l" T2 E: V
7 y# X1 P) {3 f+ ^6 R; ]) V
攻击测试:
4 W: ?- Q7 i( \
测试地址
http://localhost
6 s4 e" z1 [$ X; x' j( T% ~
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
/ f: v* H; V0 P* c2 C
; w# C) \: H$ T# k& ?% z
[attach]179[/attach]
- n. E; |1 J9 X( ~
@6 S! y( |! y
) Q! a% y3 q. M0 b& \# u
[attach]180[/attach]
7 h% l9 m: C% z
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2