첫 버그 리포트, $3000, #625404

크롬 브라우저의 버그를 작년 겨울방학에 찾았었다. 20일쯤에 UAF를 하나 터뜨렸다. 밤에 찾고 낮에는 잤다. 찾다보니 2월동안에는 추가적으로 2개의 취약점을 찾았다. 어떻게 찾았냐면…

프롤로그

2014년 12월 말에 BoB 프로젝트가 거의 끝나고 심심하던 중 “브라우저 버그를 찾고 싶다”라는 마음으로 개발자 도구에 잘 안쓰이는 자바스크립트 기능들을 입력하고 있었다. 가령 아래와 같이.

<script>
Array.prototype.toString = function() { throw []; }
alert([]);
</script>

웬걸, IE에서 널디레가 터진 스크립트였다! 왠지는 모르겠고 몇 달 뒤에 다시 확인해보니 패치가 되어있었다. 또 크롬에서는 아래와 같은 스크립트가 약간 신기하게 동작했었다.

<script>
Function.prototype.p = Function.prototype.toString;
Function.prototype.toString = function() {
    console.log(this.p());
}

chrome.app;
</script>

처음에는 Function의 toString 메서드를 바꾸고 개발자 도구에서 이것저것 해봤는데, 생각보다 많이 로그가 남았다. 그래서 페이지에 넣고 chrome.app 같은 속성들을 참조하면서 돌려봤는데, 별게 다 나왔다! 저 때는 아마 require, requireNative, 그리고 requireAsync 같은 것들이 나왔다. 또 웃긴게, 에러들도 내가 듣도 보도 못한 에러였다. Uncaught Natives disabled라니? 이런 에러는 적어도 내장 라이브러리에서는 보지 못했던 에러였던 것이다.

나로서는 궁금했다. 크롬 소스코드 검색 엔진이 있다는 것을 알고 해당 에러 메세지를 검색을 해봤다. 그랬더니 require, requireNative 함수들이 C++로 구현이 되어있었다! 뭔가 자바스크립트같은, 그러니까 v8::String이라던지 그런 클래스들을 쓰면서 말이다. 뭔가 호기심이 생겼다.

그러다보니까 chrome.app, chrome.webstore같은 여러 가지 모듈들이 C++ 및 js로 구현이 되어있다는 것을 알게 되었다. 소스를 보다보니 assert류 크래시들도 어떻게 생기는지 대충 감이 왔고, windbg도 사용해보게 되었다. 사실 크래시 자체는 일상이 된게, assert류가 많은데 나는 그냥 아무거나 다 넣어봤기 때문이였다! windbg를 붙여보면 int3같은 명령어에서 크래시가 나곤 했다.

… 그리고 6개월이 지났다. 여러 가지 일이 있고 그에 치여 사느라 정신이 없기도 하고.. 그냥저냥 하다보니 이제 함수의 toString함수를 바꿔도 저 함수들은 잡히지 않았다. 뭔가 내부적으로 패치를 했겠지..

… 그리고 다시 6개월이 지났다. 뭔가 주변의 인물들에 의해 묘한 동기부여를 받았었다. 뭐, 가령, 유투브에 브라우저별로 다 뚫은 PoC가 올라온다던지… 나라고 못할 게 뭐야! 라는 생각이 들었다. 뭔가 억울하기도 하고. 사실 해보지도 않았는데 벌써부터 내가 버그헌팅은 못한다라고 한계를 짓기는 뭐 좀 그렇잖아.

2016/01/01.

뭐 대충 그러다보니까 20일동안 컴퓨터를 잡고, 밥먹고 자면서 취약점을 찾으려고 했다. 근데 웬일이야, BoB에서 맥으로 스크립트를 만들어서 실행하는데, 크래시가 나는데, 거의 자포자기 하는 심정으로(널디레나 int3밖에 못봤었거든) lldb를 켜서 어태치를 해봤는데, 이게 웬일이야? 포인터에는 내가 시도했었던 사이트의 주소가 포인터로 들어가있고, 게다가 write AV였으니까, 아주 신기했지.

일단 네이티브 모듈을 로딩할 수 있는 requireNative 함수를 뽑기 위해서는 아래의 버그를 썼다.

https://crbug.com/603725

보다시피 제보자가 내가 아니다. 뭐 과제하고 뭐 그러다보니까 한 학기가 금방 지나가더라. 하여튼. Kudos to robwu! You did some great job on that extension system.

<iframe id=f></iframe>
<script>
function free() {
    document.body.removeChild(f); // detach!
}
for(var i = 0; i < 100; i++)
  requireNative('render_frame_observer_natives').OnDocumentElementCreated(i,  free);
</script>

거기서부터 발동이 걸렸어. 뭐 CTF는 많이 풀었으니까, 대충 어떻게 코드를 분석하는지는 알고 있었지.

그 다음에 아마 찾았던게, 7월에 버그 4개 중 3개가 패치된 상태에서 제보한 event binding쪽 버그였지. 2월 초에 찾았었어. 2주만이였지. 웃긴게 뭔지 알아? 이걸 발견할 때 첫 번째 버그는 이미 패치가 되어있었어. 대체 어떻게 발견했지.. 하면서 골똘히 생각을.. 할 시간은 없었고. 뭐.

https://crbug.com/625404 for $3000

일단 이건 내가 제보한거고, 아직 공개는 안되어있어. 하지만 구글링해보면 어떤 코드가 어떻게 패치되어있는지는 다 나와있지. stable 버전에 fix되었으니 뭐 딱히 문제는 없지 않을까? 일단 이 버그 자체가 약간 나한테는 뚫기 힘들어 보였어. free 후 사용하는 곳이 극히 제한적이였고 그쪽에 뭔가 alloc시키기는 약간 어려워 보였거든, 바이너리 단위로 분석을 해야했어. 힙이 어떻게 동작하는지도 알아야했고. 이건 보류했지.

참고로.. 이 버그를 오마주한 게 SECUINSIDE 2016의 byhuman 문제야!

그러다가 첫 번째로 발견한 이 버그같은게 있을까 싶었는데, 이건 그러니까 소스 코드를 오디팅해서 찾은거지. 아래의 버그야.

https://crbug.com/603732

3월 말에 어찌저찌 이걸 써서 메모리 릭까지 유도를 했었어. 이 버그는 malloc/free의 기본 allocator에서 난 UAF였는데, 캔버스 버퍼가 malloc/free로 할당/해제된다는 점을 써서 메모리 릭을 했었고, 또 write도 가능한가.. 싶었지. 하지만 학기 중이였고, 과제도 그렇고 정신적으로 여유가 없었어. 나는 스케쥴을 이때까지는 잘 안짰거든. 지금도 안짜. 반성하고 있지. 패치된 것을 발견하고 난 뒤에는 뭔가 되게 아쉬웠지. 대체 누가 제보했을까.. 싶기도 하고. 약간 거만하지만 난 이걸 내년에 pwn2own때까지 가지고 있을 예정이였거든. 근데 어떻게 알아서 패치를 하더라고. 음.. 뭐 처음 하는 사람한테 너무 많은 것을 바라지는 말아줘.

그리고 크래시 낸 것중에서 하나 더. Type confusion 버그였는데, 어떤 버그인지, 그리고 어떻게 패치했는지는 역시 버그 ID로 검색을 해보면 나온다구.

https://crbug.com/xxxxxx for $0

근데 이건 체이닝된 버그가 이미 패치가 된 상태에서 제보를 했으니, 익스플로잇을 하려면 개발자 도구를 켜서, 어찌어찌 해야됬었지.. 그래서 아마 리워드를 받지 못한 것 같아. 패치는 아마 안되었을거야.

6개월이 지난 후

벨루미나를 하느라 베이징에 가있던 중에 문득 내가 전에 찾았던 버그들이 이제 패치가 되었을까 궁금했어. 실행해봤고, 몇 가지는 동작하지 않았지. 흥분했어. 패치가 되었다니! 안 될줄 알았는데! 뭐.. 빨리 익스플로잇하고 낼걸 그랬나봐.

위에서 내가 찾은 버그가 아닌 것들은 금액을 안 썼어. 아이너리하게도, 2월에 저 분 말고 다른 한 분이 나랑 거의 같은 방법으로 requireNative 함수를 얻어냈고, 모듈을 뒤지다가 UXSS를 찾아냈지. UXSS.. 금액이 큰데 말이지. 뭐 사실 이걸로 나도 깨달은 것이 조금 되지. 고민하게 된 것도 조금 있고.

1. 깨달은 것?

취약점은 그냥 많이 찾아서 많이 내자. 숨기지 말고.

2. 고민하게 된 것

학기 중에 이런 일이 반복된다고 해보자. 뭐 그러면 계속 아쉬워야하나? 그리고 이런 일을 굳이 반복해야 할 필요가 있을까? 찾아놓고 제보를 안해서 돈도 적게 벌었지. 스케쥴 관리의 필요성도 느꼈어. 학기 중에 내가 하고 싶었던 일을 하면서도 과제를 할 수 있었다면 조금 더 나은 상황이였을지도 모르겠지만 이건 의미 없는 고민이야.

세상에는 아주 많은 변수가 있다고 가정을 해보자, 적어도 현재에 의해 미래가 결정된다면. 과거에 이랬으면 이라고 생각하는 것은 사이드 이펙트가 아주 커보이더라. 뭐 지금도 그렇지.

소감

앞으로 취약점을 찾으면 그냥 여러 개 찾고 여러 개 다 제보할거야. pwn2own? 그 전에 많이 찾아야지! 적어도 버그 하나가지고 불안해하지는 말자고. 그리고 조금 더 크게 생각해보자고, 생각을 했었지.

그래, 황당하면서도 재밌는 얘기야, 나한테는. 경험을 하고 느낀 점이 있어 이렇게 글로 썼어. 잘 읽어줬길 바래. 읽어줘서 고마워!