Docs

Cookie bomb

파급력과 보안 전문가들의 관심도가 반드시 일치하지는 않는다.
파급력이 크지만, 관심을 받지 못하는 공격 기법 한 가지에 대해서 다뤄보려고 한다.

그 전에 간단한 사실을 몇 개 짚어보자.

HTTP 프로토콜은 Request와 Response로 나뉘어지고, Request와 Response는 헤더와 바디로 구분된다.
Request 헤더가 너무 클 경우에 서버에서는 413 Request Entity Too Large, 400 Bad Request 등의 에러를 응답한다.
크기 제한은 서버마다 다르지만 대체로 8kb가 넘어가면 대부분의 서버에서 에러를 응답한다.

  • Apache : 8kb
  • nginx : 4kb – 8kb
  • IIS : 8kb – 16kb
  • Tomcat : 8kb – 48kb

여기서 Request 헤더에는 어떤 값이 들어갈까?
무수히 많지만 가장 흔히 사용되는 헤더는 이전 웹 페이지 주소를 의미하는 referer, 클라이언트의 브라우저를 의미하는 user agent, 그리고 쿠키등이 있다.

쿠키에 초점을 두어보자.

서버에서 클라이언트에 내려주는 작은 텍스트 조각을 쿠키라고 부른다.
브라우저는 쿠키를 저장해두었다가 HTTP Request를 보낼 때 해당 쿠키 조각을 헤더로 전송한다.

이 쿠키가 비정상적으로 크면 어떤일이 생길까?
Request 헤더가 너무 커져 사용자가 해당 웹사이트에 아무리 접속을 시도해도 에러메세지만 볼 수 있을 것이다.

자 그렇다면.
만약 공격자가 웹사이트의 취약점을 사용해 방문자의 브라우저에 비정상적으로 큰 쿠키를 생성해줄 수 있다면,
희생자는 쿠키가 삭제될때까지 그 웹사이트를 사용할 수 없게 된다.

어떻게 타 사이트의 쿠키를 생성해줄 수 있을까?

먼저 우리는 XSS 취약점을 사용할 수 있다.
아래와 같은 코드를 삽입했을 때 해당 스크립트를 실행한 클라이언트의 브라우저에는 4kb짜리 쿠키 100개가 생성될 것이다.

var base_domain = "example.com";
var pollution = Array(4000).join('a');
for(var i=0;i<100;i++){
  document.cookie='bomb'+i+'='+pollution+';domain='+base_domain;
}

이 400kb짜리 쿠키 폭탄은 HTTP Request 헤더에 포함되어 전송되며, 이정도 크기의 헤더를 받아주는 서버는 찾아보기 어렵다.
이제 희생자는 example.com에 접속할 수 없다.

Cookie bomb을 위해서 반드시 XSS를 사용해야 하는것은 아니다.
검색 기록, 유저 트래킹 등의 간단한 동작을 위해서 클라이언트에 쿠키를 내려주는 행동은 흔하며,
내려주는 쿠키에 유저 인풋이 아무런 필터링 없이 포함된다면 Cookie bomb이 가능하다.

이제 파급력을 더 키워보자.

example.com 하위에 많은 서브도메인이 있다고 가정하자.
a.example.com
b.example.com
c.example.com…
xss 취약점이 a.example.com에서 발생했을 때 쿠키의 domain을 a.example.com이 아닌 .example.com 으로 설정하면 example.com의 모든 서브도메인에 접근이 불가능해진다.

또한 쿠키의 수명인 expires를 크게 주면 쿠키가 만료되기까지 더 오래 걸릴것이다.

그렇다면 방어자들은 이 공격에 어떻게 대처하고 있을까?

먼저 Facebook에서 해당 페이로드를 실행해보았다.

아무런 힌트 없이 오류를 반환한다.
일반인이 이 오류 메세지를 보고 쿠키 삭제를 떠올릴 가능성은?
없다.

트위터, 넷플릭스, 네이버 모두 아무런 대비가 되어있지 않다.

하지만 구글은 달랐다.

위와 같은 413 오류를 반환한 뒤 메인페이지로 돌아가고, 즉시 정상적으로 서비스의 이용이 가능했다.

분석결과 아래와 같은 코드가 413 오류 페이지에 심어져 있었다.

(function() { /*
 Copyright The Closure Library Authors.
 SPDX-License-Identifier: Apache-2.0
*/
var c=function(a,d,b){a=a+"=deleted; path="+d;null!=b&&(a+="; domain="+b);document.cookie=a+"; expires=Thu, 01 Jan 1970 00:00:00 GMT"};var g=function(a){var d=e,b=location.hostname;c(d,a,null);c(d,a,b);for(var f=0;;){f=b.indexOf(".",f+1);if(0>f)break;c(d,a,b.substring(f+1))}};var h;if(4E3<unescape(encodeURI(document.cookie)).length){for(var k=document.cookie.split(";"),l=[],m=0;m<k.length;m++){var n=k[m].match(/^\s*([^=]+)/);n&&l.push(n[1])}for(var p=0;p<l.length;p++){var e=l[p];g("/");for(var q=location.pathname,r=0;;){r=q.indexOf("/",r+1);if(0>r)break;var t=q.substring(0,r);g(t);g(t+"/")}"/"!=q.charAt(q.length-1)&&(g(q),g(q+"/"))}h=!0}else h=!1;
h&&setTimeout(function(){if(history.replaceState){var a=location.href;history.replaceState(null,"","/");location.replace(a)}},1E3); })();

beautifier를 돌린 결과는 아래와 같다.

(function() {
    /*

 Copyright The Closure Library Authors.
 SPDX-License-Identifier: Apache-2.0
*/
    var c = function(a, d, b) {
        a = a + "=deleted; path=" + d;
        null != b && (a += "; domain=" + b);
        document.cookie = a + "; expires=Thu, 01 Jan 1970 00:00:00 GMT"
    };
    var g = function(a) {
        var d = e,
            b = location.hostname;
        c(d, a, null);
        c(d, a, b);
        for (var f = 0;;) {
            f = b.indexOf(".", f + 1);
            if (0 > f) break;
            c(d, a, b.substring(f + 1))
        }
    };
    var h;
    if (4E3 < unescape(encodeURI(document.cookie)).length) {
        for (var k = document.cookie.split(";"), l = [], m = 0; m < k.length; m++) {
            var n = k[m].match(/^\s*([^=]+)/);
            n && l.push(n[1])
        }
        for (var p = 0; p < l.length; p++) {
            var e = l[p];
            g("/");
            for (var q = location.pathname, r = 0;;) {
                r = q.indexOf("/", r + 1);
                if (0 > r) break;
                var t = q.substring(0, r);
                g(t);
                g(t + "/")
            }
            "/" != q.charAt(q.length - 1) && (g(q), g(q + "/"))
        }
        h = !0
    } else h = !1;
    h && setTimeout(function() {
        if (history.replaceState) {
            var a = location.href;
            history.replaceState(null, "", "/");
            location.replace(a)
        }
    }, 1E3);
})();

24라인을 보면 Cookie의 길이가 4E3(4000)을 넘을 경우에 특정 함수를 실행하며,
해당 함수에서는 쿠키의 expires를 1970년으로 변조해 모든 쿠키를 삭제한다.
즉 413 오류 페이지에 삽입된 코드는 쿠키의 길이가 4000이 넘는지 확인하고, 4000이 넘으면 쿠키를 삭제하는 행동을 한다.

Cookie bomb 공격을 타겟팅해서 작성된 정확한 미티게이션이다.
Google drive, Hangout, Youtube 등 구글의 모든 서비스에서 동일한 코드를 반환한다.

그렇다면 이러한 미티게이션은 Cookie bomb을 완벽하게 방어할 수 있을까?

쿠키에는 HttpOnly 라는 플래그가 있으며 해당 플래그는 JavaScript에서 쿠키에 접근하는것을 막는다.
HttpOnly 플래그가 설정되어 있으면 위의 코드로는 Cookie bomb을 방어할 수 없다.

물론 JavaScript를 사용해서는 HttpOnly 플래그가 설정된 쿠키를 생성하는것이 불가능하다.
하지만 XSS가 아닌 서버측에서 쿠키를 할당해주는 기능을 악용한 Cookie bomb 공격이 가능할 때,
해당 기능에서 쿠키를 HttpOnly 플래그를 설정한채로 할당해준다면 위의 JavaScript 코드를 우회 가능하다.

덧붙임) 자신의 브라우저에서 실습을 원한다면 크롬 시크릿 모드를 활용하시길 권장합니다.

Reference

https://homakov.blogspot.com/2014/01/cookie-bomb-or-lets-break-internet.html
https://blog.innerht.ml/tag/cookie-bomb/

Leave a Reply

Your email address will not be published. Required fields are marked *