MySpace Samy 웜 분석
오늘은 첫번째 SNS 웜으로 기록된 Samy 웜에 대해서 알아보겠습니다. 2005년 10월 4일, Samy라는 아이디를 쓰는 MySpace 사용자는 웜을 이용해서 하루만에 90만명이 넘는 친구(인증샷)를 확보했습니다.
- MySpace는 많은 수의 태그를 차단하고 있다. <a>, <img>, <div>와 <embed> 등 몇 개의 태그만 허용되는 것 같다. <script>나 <body>, onClick이나 on으로 시작되는 어떤 것도, href에 자바스크립트를 넣는 것도 안 된다. 그렇지만 일부 브라우저에서는 CSS 태그에 자바스크립트를 쓸 수 있다. (IE나 사파리 일부 버전 등) 아무튼 자바스크립트가 동작해야 한다.
예제: <div style="background:url('javascript:alert(1)')"> - 이미 작은 따옴표와 큰 따옴표를 다 써버렸기 때문에 자바스크립트 코딩하기가 굉장히 까다로워졌다. 이를 우회하기 위해 표현식에 자바스크립트를 저장해놓고 이름으로 불러서 실행하는 방법을 사용했다.
예제: <div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')"> - 음 좋다. 이제 작은 따옴표와 자바스크립트를 사용할 수 있게 되었다. 그렇지만 MySpace는 javascript가 발견되는 족족 전부 날려버린다. 이를 우회하려면 일부 브라우저에서 "java\nscript"도 "javascript"로 인식한다는 점을 이용한다. (즉, java와 script 사이에 개행이 일어나게 하는 것이다.)
예제: <div id="mycode" expr="alert('hah!')" style="background:url('java
script:eval(document.all.mycode.expr)')"> - 됐다. 작은 따옴표도 쓸 수 있고, 가끔 큰 따옴표가 필요하긴 하겠지만 이스케이프 하면 된다. 음 그런데 MySpace는 모든 이스케이프된 따옴표를 날려버린다. 그렇지만 자바스크립트를 이용해서 정수를 아스키로 바꾸는 트릭으로 따옴표를 문자열에 붙이면 된다.
예제: <div id="mycode" expr="alert('double quote: ' + String.fromCharCode(34))" style="background:url('java
script:eval(document.all.mycode.expr)')"> - 내 프로필을 보는 사용자의 프로필에 코드를 집어넣으려면, 다른 사용자의 주소를 알아내야 한다. 아, document.body.innerHTML을 이용하면 페이지를 보고 있는 사용자의 ID를 얻어낼 수 있다. MySpace는 innerHTML 문자열도 다 날려버렸다. 또 돌아가면 된다. eval()을 이용해서 두 개의 문자열을 하나로 합치는 방식으로 "innerHTML"을 만들자.
예제: alert(eval('document.body.inne' + 'rHTML')); - 이제 다른 페이지를 가져와야 한다. iframe을 사용할 수도 있겠지만, iframe은 숨겨놓는다고 해도 사용자가 무슨 일이 일어나는지 눈치채기 더 쉽다. 그러니 대신 AJAX (XML-HTTP)를 사용해서 HTTP GET이나 POST 요청을 보내려고 한다. 그런데 또 MySpace는 XML-HTTP를 쓰는데 필요한 "onreadystatechange"를 날려버린다. 또 eval을 써야 한다. 그리고 여기에 덧붙여서 쿠키 정보도 같이 보내야 한다.
예제: eval('xmlhttp.onread' + 'ystatechange = callback'); - 이제 다른 사용자의 프로필을 GET으로 가져올 때다. 여기서 현재 영웅 목록을 가져올 수 있다. 이 영웅 목록에 나를 살짝 끼워넣으려고 한다. 이건 뭐 XML-HTTP 요청을 보내기만 하면 되는 것이지만 내 프로필을 보고 있는 사용자의 friend ID가 필요하다. 위에서 했던 것처럼 내 프로필 페이지에서 찾으면 될텐데.. 문제는 검색할 때 내가 만들어 넣은 코드에서 같은 검색 문자열을 찾게 될거란 말이다. 가령 이 페이지가 'foo'를 포함하는 경우에 특정한 일을 하도록 하려고 하는데 검색해봐야 내가 넣은 코드의 검색 문자열 'foo'가 다시 나오게 되므로 항상 참이 되어버린다. 이 문제를 해결하려면 검색 문자열 자체도 쪼개놓고 실행할 때 붙여서 쓰면 된다.
예제: var index = html.indexOf('frien' + 'dID'); - 이제 영웅 목록을 가지고 있는 상태이다. addFriends 페이지에 XML-HTTP POST 요청을 보내어 나를 친구로 추가하도록 하자. 읍, 그런데 제대로 동작하지 않는다. 이게 어찌 된 일인가? 내 프로필 페이지는 profile.myspace.com이고 POST 하는 페이지는 www.myspace.com이었다. 도메인 이름이 다르므로 XML-HTTP로 GET이나 POST 요청을 보낼 수가 없었다. 이를 우회하기 위해 www.myspace.com의 동일한 URL로 옮겼다. 어차피 www.myspace.com에서도 프로필을 볼 수 있기 때문이다. 만약 profile.myspace.com으로 프로필을 보러 왔다면 www.myspace.com으로 먼저 리다이렉트한다.
예제: if (location.hostname == 'profile.myspace.com') document.location = 'http://www.myspace.com' + location.pathname + location.search; - 드디어 POST가 된다! 그런데 요청을 보내긴 했지만 친구가 실제로 추가되진 않았다. 이건 MySpace가 POST에 사용할 랜덤한 해시 값을 만들어 놓기 때문이다. (역주: CSRF 공격을 막기 위한 것이죠.) POST에 올바른 해시 값이 같이 들어오지 않으면, 그냥 무시해버린다. 이를 우회하려면 브라우저인 척 해서 페이지를 받은 다음 파싱해서 해시 값을 얻어오면 된다.
- POST로 친구 추가가 끝나면 영웅으로도 추가하고 웜 코드도 복제해서 박아넣어야 한다. 해시 값을 새로 받아오는 것만 제외하면 영웅으로 추가하는 것도 POST 한 번으로 끝낼 수 있다. 자기 복제에 필요한 코드는 현재 프로필 페이지의 소스를 가져와서 코드 부분만 파싱한 다음 POST로 넘기는게 제일 쉬운 방법이다. 아, POST가 제대로 되려면 URL-인코드랑 이스케이프도 해야 한다. 어 그래도 이상하게 동작을 안 한다. 자바스크립트의 URL 인코딩과 escape() 함수는 제대로 이스케이프 처리가 안 되는 것 같다. 그래서 수작업으로 일부 이스케이프 처리를 해주었다. 자 이제 자기 복제가 가능한 웜을 만들어냈다!
- 길이 제한 등 다른 문제 때문에 코드를 빡세게 만들어야 한다. 불필요한 공백도 다 없애고 이름 길이도 최소한으로 하고 함수도 최대한 재사용 하는 등..
by xeraph | 2008-11-01 15:39:55 | 미분류 | 트랙백 (2) | 덧글 (2)
트랙백 주소 : http://nchovy.kr/forum/2/article/297/trackback
Tracked from gendoh's me2DAY 2008-11-03 10:21:06
MySpace 에서 뻘짓하기 : 내용 읽어가다 보니 뒷목이 뻣뻣하다.
Tracked from kkung's me2DAY 2008-11-12 13:04:00
최초의 SNS웜 MySpace Samy 웜 분석





랜덤한 해쉬값을 어떻게 얻어오는건지에 대해선 대충 설명해서 어떻게 하는건지 궁금하네염..; Myspace 를 안써봐서 어떤식으로 만드는지..; 그나저나 웹 보안은 진짜 자칫잘못하면 뚫리기 쉽상인것 같다는;;
그냥 친구 추가할 때 쓰이는 페이지 (POST 보내는 폼이 있는 페이지)를 XML-HTTP로 가져오면 되지~