|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告% B {8 E: ]% B% Z/ G8 ?
漏洞作者:skysheep
9 p8 w* ~$ N8 b$ }分析作者:Seay# ?6 n( |, p# T7 K4 m1 _
博客:http://www.cnseay.com/
+ [# @; V' g/ a漏洞分析:( @/ e1 A" x* K2 O# ^* k
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
! N6 M/ U, m. a& u, y3 t" j
& x7 V. [* E2 V3 a, P# ~( ~3 n$ }) L' f3 s. T& k
1 C( `8 x) T; J" @' l
public function account_manage_info() { 4 }% I6 A3 f8 U! o0 ~
if(isset($_POST['dosubmit'])) {
6 a$ _2 G6 b, p. D //更新用户昵称
' m( @; b) f# T+ D6 b* e6 Z+ L. n k $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
1 j- S, O# u4 R e if($nickname) {
) N' e) B& H5 ?5 j* {# U; o $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
K& b7 ^; v+ ?0 z8 S if(!isset($cookietime)) { * m' ]7 O% u) d$ G+ v
$get_cookietime = param::get_cookie('cookietime'); 5 T, R( G2 D3 c! K1 i3 _' Z
} # K5 `( K1 _' I& }; n+ S4 O' w" [
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 7 R; _5 P) h- c
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 8 \, u) m. L+ c3 o: t! `" A
param::set_cookie('_nickname', $nickname, $cookietime);
: j; D! U) ^: z5 l% y5 W } 4 b4 a: v. O! b
require_once CACHE_MODEL_PATH.'member_input.class.php'; & [! j$ z, \; ]6 \ l" p3 [
require_once CACHE_MODEL_PATH.'member_update.class.php';
# @: F+ P2 }, K+ h# z $member_input = new member_input($this->memberinfo['modelid']);
! g% Z9 T/ H, k3 p. Y, J' D $modelinfo = $member_input->get($_POST['info']);
- c' S/ ?3 s+ {$ h $this->db->set_model($this->memberinfo['modelid']);
( U( v2 K6 D4 Y2 Y" y' s $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
) T' c! A1 |0 {/ f# Y+ u8 m2 O# n) W7 U if(!empty($membermodelinfo)) { 8 c/ k" l, G6 p" U4 U+ z4 y" {$ H! b
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); , U1 K, C. A7 N+ n( Z
} else {
/ k7 v) o/ ^2 E3 m $modelinfo['userid'] = $this->memberinfo['userid']; 6 S3 k8 I a: {( h5 Z; i+ n0 @
$this->db->insert($modelinfo);
5 `+ m; P6 m6 [& I8 v% @4 M4 t" q }
' S e5 o6 r3 ]. S代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,& B6 o3 n, j, E/ T8 I( Q
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( Z. D) W! s2 g+ p( r3 X4 r5 g3 w6 K" J3 a" ]% q( \* V* A
. R, d6 k7 k N( |: D. L4 h5 {
N. }3 g. V: y# ]- l9 Ofunction get($data) {
7 \% S' _' @, r4 R2 c8 u $this->data = $data = trim_script($data); , I; j! s9 @; H+ u& P& ^
$model_cache = getcache('member_model', 'commons'); N H2 f0 m8 A3 [* f
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
3 h9 \: a8 |; _ $info = array();
( \# k6 q. `$ R$ {7 D $debar_filed = array('catid','title','style','thumb','status','islink','description'); 4 z; c+ J) x1 Z3 u- a0 h
if(is_array($data)) {
: t, ^, `+ Z2 o$ n) e; }, p foreach($data as $field=>$value) {
. T1 `, Y" S/ b- D8 z& S# W7 d if($data['islink']==1 && !in_array($field,$debar_filed)) continue; 3 {, ~0 G. B! Q2 P2 D8 d" t7 B
$name = $this->fields[$field]['name'];
% C# Q/ p( E9 S8 t4 P2 s1 Q $minlength = $this->fields[$field]['minlength']; 3 D5 G2 H7 ?( e4 j" _/ d
$maxlength = $this->fields[$field]['maxlength'];
) D& c5 a, _6 q1 _3 y $pattern = $this->fields[$field]['pattern']; 0 n$ D; Q# {- M' A5 K3 G
$errortips = $this->fields[$field]['errortips'];
! P- I. B! t( N) s! ] if(empty($errortips)) $errortips = "$name 不符合要求!"; 7 p" G! @3 ~8 m
$length = empty($value) ? 0 : strlen($value);
$ ?2 z6 q; d0 s if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
' d: C# r- i8 c# G" T/ T if($maxlength && $length > $maxlength && !$isimport) { 9 [ U& O7 @. [5 B' @% ]5 j
showmessage("$name 不得超过 $maxlength 个字符!");
8 z( e7 H# d1 e1 h+ r } else {
( l' n9 K+ f g: d i# K5 K8 \ str_cut($value, $maxlength);
5 t0 h6 V& V2 ?/ B9 P }
: A. m* t. z8 o, V( a; w& s0 O if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); $ Y$ M+ ~5 b5 b- u6 f
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
) a5 P6 T& t6 b; W5 a$ a% [ $func = $this->fields[$field]['formtype']; / n3 ~. _/ B/ E0 }. @& J! S% L
if(method_exists($this, $func)) $value = $this->$func($field, $value); ! U, y8 F! W* n) i" X
$info[$field] = $value;
; ]* R+ H/ Q, x- p, E0 a7 N" h }
% b2 V* b- R' P% M6 X }
2 i3 o9 a/ O% V$ t return $info;
9 b( k7 l" n% H/ i } . a* p. N+ {* ]; z8 I/ N
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
2 ?( _% L; A" W! ^0 g
6 ?7 v+ M2 m/ J3 e, u再到phpcms\modules\member\index.php 文件account_manage_info函数
0 ]( s" h+ @4 ?9 j' y7 M过了get()函数之后。
% q3 ~, @5 {& E* S/ _% k
4 r2 W9 i9 Q8 | N1 v/ z/ `( }
: U; ^! m- ~- x* G3 p, {# p$modelinfo = $member_input->get($_POST['info']);
! r0 M" {; l% J) c* |# H3 X. b $this->db->set_model($this->memberinfo['modelid']);
# a8 Z. C! J1 l+ C& X $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" H3 D0 t4 c0 N0 S T if(!empty($membermodelinfo)) {
% U; ^8 K& [6 [& [ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); / |- c J+ C" S+ z' k& o6 i
} else { 1 Y4 j- O+ _ W4 } b9 d- {, K0 f0 L
直接带入数据库,update函数我们跟进看看
$ K% ^- z* r( o$ t7 P0 b1 I9 T: }$ x( z1 _
, Z3 [' C1 d9 { ^
public function update($data, $table, $where = '') { / A4 ~ A x/ h4 c) k
if($table == '' or $where == '') {
% `/ P' }3 f1 t& y* Q5 D return false;
$ h. o5 O# `5 M4 ?6 y, t0 v0 x }
) x4 ]5 e' j" D $where = ' WHERE '.$where; ' P6 g1 E; ?0 O4 t9 z9 _7 C
$field = '';
$ K; ?6 q$ H' H4 Q$ y, u if(is_string($data) && $data != '') {
$ F; ^* M5 ?4 r1 u) I( O5 b& b $field = $data;
2 f* C- u8 U) }" H } elseif (is_array($data) && count($data) > 0) {
6 K* {" u2 z+ D2 W4 d+ Z, Y $fields = array();
6 e/ p( ^ V2 A% B( `- L foreach($data as $k=>$v) {
4 |* x# e3 K( N k* |$ o switch (substr($v, 0, 2)) {
; E" C ?8 e2 i case '+=': . l5 U+ \% _ _; B) ?
$v = substr($v,2); 6 J& V4 F$ e6 A: e2 W) x
if (is_numeric($v)) {
) J! p" z* P4 u: B $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
7 r6 a) U. g) h+ P5 U7 y } else {
2 X' X8 V* Q# ]0 ]) d: \* f continue;
+ ?' B# f+ A1 K3 f) i } ) Z+ q9 s& W- C- n4 x; X$ o
break;
1 q) E4 S# x* ~8 U4 o$ n case '-=': N* j+ J5 n: N
$v = substr($v,2); " N! d3 L6 j8 Y' l
if (is_numeric($v)) {
% E; g7 k# `: M $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
' d$ P; n& B9 h( r } else {
+ w" o* m4 |+ W5 r4 r5 c( | continue;
. \9 x4 D8 N! w) m- { } 1 ~3 D9 s# Q* I& I
break;
}3 h4 R# ~; o( A4 U default:
4 B+ M* I* w5 t% A1 y! ^ $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 9 c$ G3 _% k; t1 v4 i
}
) M/ R7 U! C% ~' t" u4 w& X }
" s* q; v8 i( V; t) c $field = implode(',', $fields); 7 o: D& n% w( b4 ` r0 ?% X
} else {
7 w- n- ~% Y: k/ D. B return false; ' K5 m) Y; @6 B
}
9 }/ ^6 n' p4 v" T1 y5 Y $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; - X# h/ p8 P, @ a/ H! `/ `
print_r($sql);
0 G7 E7 r, s$ N% ~ return $this->execute($sql);
" L. p: Q( v: \, P+ D( s0 N% t# S }
3 C- K' c; g$ A- K/ v) q8 E) d( q从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。- `% U, C/ D# O. p! C
" m( _ X3 e/ K w% C, Z
攻击测试:3 q( ]) U; o5 u& ~
测试地址http://localhost2 K9 H2 {' Y0 x
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句, B+ C, z% _# D3 @! ?! D
1 ~ y3 j: K) Z+ I5 v% H0 Z3 p3 r
: Z, [3 X/ y/ V! r1 L/ ?/ n6 C6 ~ z$ D. l( w7 R) v4 V' d1 }
6 l- W5 H' V8 c& v5 @' ^" s0 _
' J- r; A N. N3 s |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|