|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
% F% R0 S0 |) }3 Y2 X1 {4 F, c漏洞作者:skysheep
( t0 d! u3 S1 z8 {8 B2 C分析作者:Seay
: D" A: K( f& H& d" h8 s博客:http://www.cnseay.com/
" b/ }# E9 \5 J. g( q9 d漏洞分析:! f2 b% c$ _, ?. d' d8 w% h0 w3 }8 U
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 M6 w$ r# z1 Q/ C9 v9 ~) m+ i9 f& a( E5 o" V
# C7 M7 U2 @ m6 s3 c' W+ K6 \' N 1 o- |. Y* U6 Y
public function account_manage_info() {
" [$ R, G8 g/ ]( o if(isset($_POST['dosubmit'])) { ; x" x3 t9 d9 \5 w" Q! P |% Y5 w0 Y
//更新用户昵称 - ]% E1 Y5 m p$ W9 j6 ~# F
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; # j" I- S3 [6 R) p6 e
if($nickname) { 5 R' I0 T: g1 ^' K/ l, E; A
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
- |. c$ ~; D y7 T. L5 N/ H if(!isset($cookietime)) {
0 T& G G* D9 j# ~5 |1 ^- \0 W; t $get_cookietime = param::get_cookie('cookietime');
, z) T9 m7 _6 G }
% k4 @5 z& o3 {8 v $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
# i" y; t8 E: a3 I $cookietime = $_cookietime ? TIME + $_cookietime : 0;
& Z/ i& s% f2 p/ @( ~ param::set_cookie('_nickname', $nickname, $cookietime);
% \2 l( H$ B& a1 R2 a }
% X% B, A1 K' T+ C1 U) m) w: s- t require_once CACHE_MODEL_PATH.'member_input.class.php';
! @7 f' B( m6 ~! m require_once CACHE_MODEL_PATH.'member_update.class.php';
, B0 M8 M7 _" n2 K ` $member_input = new member_input($this->memberinfo['modelid']);
$ v2 s% H! b2 X& F $modelinfo = $member_input->get($_POST['info']); $ q+ d) p* ?1 C# u# b: c o
$this->db->set_model($this->memberinfo['modelid']); * v/ K8 I& O$ g+ K: n7 x8 {
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
! J y. r, E0 R& c if(!empty($membermodelinfo)) { 5 e8 o7 |, [6 u5 i
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
4 s: l" ~% q! l+ J) G } else {
1 \+ \& A% Y# o; L9 M $modelinfo['userid'] = $this->memberinfo['userid']; 0 b, S8 h6 J8 T% ], ]0 Y7 V! E3 T# B
$this->db->insert($modelinfo);
' [# T4 r' \) Q } + R/ y' u* U; J
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看, w( h( n5 a) a3 }- |' i
在\caches\caches_model\caches_data\ member_input.class.php 文件中:. p- f) w" }' ]. r3 u
5 ~1 L2 b I) Q. u
9 [: C% x& P; A* y3 P 2 f7 z: F% Y& B( E! J& a
function get($data) {
/ U4 {2 D6 ~. N5 S1 n6 r2 l $this->data = $data = trim_script($data); 9 p+ K7 {, r. E* r
$model_cache = getcache('member_model', 'commons'); + S$ W8 y) ~4 b2 `( v% X
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 0 G* m$ _! _( ^1 V% a
$info = array(); K; Y; Y4 h' E! \8 N- a
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 5 t2 I! N8 I( J2 v0 n
if(is_array($data)) { - I, W1 o# C, _6 b' n
foreach($data as $field=>$value) { , a* {) x+ p% ^/ {7 D: ~
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
3 ~' T. }3 a' c $name = $this->fields[$field]['name'];
9 Q% y$ ^) |6 ]! } $minlength = $this->fields[$field]['minlength']; ) l4 e$ r* O9 i. C" v
$maxlength = $this->fields[$field]['maxlength'];
7 f/ i% o7 R" X7 N0 K& p6 @ $pattern = $this->fields[$field]['pattern']; 1 V7 Y$ f' Q; _. u! t
$errortips = $this->fields[$field]['errortips'];
0 T/ d( v# {5 d* d6 _! o! D if(empty($errortips)) $errortips = "$name 不符合要求!"; - Q, ~! D3 I9 T# T! G4 M( B- T4 T
$length = empty($value) ? 0 : strlen($value); E: m% a7 l; C1 V0 q: G
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
2 t3 E. ^9 ^; X- | if($maxlength && $length > $maxlength && !$isimport) { ]1 e! r f" m$ d# N; b
showmessage("$name 不得超过 $maxlength 个字符!"); . I$ W! s) Y0 I1 g! n3 K
} else {
+ P; V# x1 [- e# y str_cut($value, $maxlength); : U5 C% R- c6 J5 o3 y# c, Z
}
+ q/ @8 K3 I3 K8 o1 ~* r5 ~' m if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
7 } m; O% Q3 C if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 7 ^+ w; m$ D# \ L# O
$func = $this->fields[$field]['formtype'];
& c3 K5 X/ r( O% { if(method_exists($this, $func)) $value = $this->$func($field, $value);
! r8 m R# W6 l# m) G8 |, ` $info[$field] = $value; 5 j) B* Z- V# ~' n+ u
} 7 d+ B4 l5 v& `0 j7 M* w
} . N, n7 U' h$ g, e k
return $info;
, W% ?2 {; \) r6 P: S, E } 7 M2 @& r' a; n2 d) K$ }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' ?4 |. {7 j0 d$ ^1 }$ h. f- [( n9 n6 r! L6 u
再到phpcms\modules\member\index.php 文件account_manage_info函数
* A% X7 U" [3 X7 j) i9 a* f5 f$ H过了get()函数之后。
; I3 @- A" _# O" B. K7 N/ m! H- A5 T( r' I3 ]% H/ S
# C' A" W# \4 h: A4 _4 K$ L$modelinfo = $member_input->get($_POST['info']); ; z1 m F% y9 z; J
$this->db->set_model($this->memberinfo['modelid']); - i/ }! ^5 h4 X& I& P1 l
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 w+ f1 T/ y& e+ J if(!empty($membermodelinfo)) { 2 d5 k4 H: p8 S3 y6 Y- G( E' o
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); * L$ l9 e! B8 D5 l) Q6 ]3 V- p
} else { & m' @8 \( l- j) G
直接带入数据库,update函数我们跟进看看
% M6 q! H6 D" \( c% b. T
# X% c9 L2 c/ `( r- S4 ? ) R9 V h# H( X2 m$ Z% l3 P
public function update($data, $table, $where = '') {
8 s, x3 W% y5 D6 Y# F; S if($table == '' or $where == '') {
8 l! J8 j* y: \. P! Q return false; ; S7 j; K/ @" y$ t A
}
; O" e" g! m! E $where = ' WHERE '.$where; 5 i: P s) y3 Y1 M8 M8 o* e5 C
$field = ''; 9 v! s4 @2 q( ^: j5 {% ?: a6 L
if(is_string($data) && $data != '') {
) I, H/ V: I% U $field = $data;
6 C. M3 X( k+ }$ m. a( p } elseif (is_array($data) && count($data) > 0) { ! A, D' O+ ?5 ~" z" X8 B( Z. v; n6 R4 a2 L
$fields = array();
5 X, X& ]4 z3 ^3 L, z$ n8 x4 {; Y foreach($data as $k=>$v) {
$ ~& e& R) l* y3 l switch (substr($v, 0, 2)) {
+ J; L, v4 {6 M) H# s* X case '+=':
% U+ d3 k2 p3 p A( I& }. L $v = substr($v,2); 4 _1 o' I' z1 x( j% K3 _1 C' D# s/ g
if (is_numeric($v)) {
3 ]& v; }. X3 G1 w, x1 Y $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
# _% w* T1 c, [' O } else { ) f( J2 F# Y+ j: P% e& W
continue; % e8 N2 X( W- D+ h
} # N0 s# T* L+ Q6 o! Q- |) A: K' E
break;
4 X/ ?# \* Z# l case '-=': : |, u D, ]$ N0 g; B, [- z
$v = substr($v,2);
+ L- @: Q$ x- V7 M0 D( Z( @ if (is_numeric($v)) {
! U! e7 Y* W J" M$ G& |' T' w% x+ e $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 8 ^- g$ ^+ l9 Z
} else { ; Z& h& h( M$ b* y$ F N' P* @0 S, G
continue; ) f/ _( N( x9 |( ?, g! Y9 N ~
}
6 v' z0 Q+ r- ^+ I; N break;
: u3 N/ y0 S. j: @$ p8 C# ?9 d; q default:
4 K S" P: O, X t% r6 y# H; Y $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
. h1 y6 b/ G2 g0 S4 A# B } ; W* ]: L2 D- }* o
}
5 C) I3 ~- A, ~0 G $field = implode(',', $fields);
% Y" Y9 z* Q9 z+ z/ r; C } else { , F+ ~2 |& {- Y; i
return false;
' e* R: N2 c' P6 t. c; r$ W }
2 d6 G& l# D5 z' o0 e* E# n4 k( R $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 9 e3 l5 ^1 }) f6 X
print_r($sql); x7 F/ e9 b# S' B0 Y# T# O0 ?
return $this->execute($sql);
% |7 O% u/ k3 F0 W4 }3 R# ]! q } . p6 t @+ M7 ]3 ~& ~$ ]' f8 K a/ r
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。' N' x7 Z6 N- E. O
9 {2 s- q9 r) C* u
攻击测试:( Z f# D/ {1 Q/ u2 l
测试地址http://localhost
. h$ F& ^! O. n 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# G8 V+ x, P& ]! }4 ]+ B% ^* |3 m& x9 s9 I& U5 R
$ H O" }8 u, n3 R' g; d6 h5 T
# X' ?& i! _' O* `! s. I& \3 m9 ]8 w
- E! X. a& v1 |% w
& U& W6 c" `' ~3 E! E |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|