CORS Bypass via dot
Origin 헤더와 ACAO(Access-Control-Allow-Origin) 헤더는 Cross-Origin 관계에서 데이터를 전달하고 수신하기 위한 헤더로 SOP(Same-Origin Policy)를 공식적으로 우회하기 위한 헤더입니다. 일반적으로 CORS라고 통용되어 부르며, 이는 JSON Hijacking과 CSRF 취약점에 큰 접점을 가지고 있습니다.
오늘은 CORS 규칙에서 개발자가 쉽게 할 수 있는 실수와 이를 식별하는 방법에 대해 이야기하려고 합니다.
Origin Validation
Origin 헤더를 기반으로 ACAO 헤더를 내려주는 경우 해당 Origin에서 데이터를 통제할 수 있는 권한을 가지게 됩니다. 이 경우 브라우저가 Javascript로 Response를 통제할 수 있게 허용해주는데 무분별하게 허용한다면 악의적인 사이트에서 사용자의 정보 등을 가로챌 수 있기 때문에 우리는 Origin 헤더를 보고 요청의 출처를 검사하고, 우리가 예측 가능하고 신뢰하는 도메인에서만 이를 허용해줍니다.
Origin: trusted.com (O)
Origin: attacker.com (X)
그리고 보통 이러한 Origin 검사 로직은 여러 케이스를 처리해야하기 때문에 정규표현식을 기반으로 많이 작성합니다.
config.middleware.insert_before 0, Rack::Cors do
allow do
origins /^https:\/\/[0-9]{1,6}\.apps\.trusted\.com/
resource '*', headers: :any, methods: %i[get post head]
end
end
Origin: https://123.apps.trusted.com (O)
Origin: https://123.google.com (X)
Dot Mistake
dot(.
)은 정규표현식에서 개행문자를 제외한 모든 문자를 매치하는 메타문자입니다. 그러나 종종 정규표현식을 사용하다 보면 dot(.
)의 존재를 잊게됩니다. 자연스럽게 도메인 주소를 집어넣게 되고 아래와 같은 코드가 작성될 수 있습니다.
config.middleware.insert_before 0, Rack::Cors do
allow do
origins /^https:\/\/[0-9]{1,6}.apps.trusted.com/
resource '*', headers: :any, methods: %i[get post head]
end
end
언뜻 보기엔 문제가 없어 보이지만, dot의 존재로 인해 apps[임의문자]trusted.com
과 같이 dot 자리에 임의 문자가 들어간 Origin도 정규표현식을 통과하게 됩니다.
Origin: https://123.apps.trusted.com (O)
Origin: https://123.google.com (X)
Origin: https://111.apps1trusted.com (O)
결과적으로 apps1trusted.com
이란 도메인을 소유하게 되면 Origin 검사 정책을 무력화 시키고 JSON Hijacking 같은 공격을 성공할 수 있게 됩니다.
How to Check
방법은 간단합니다. Origin 체크 시 dot 부분에 임의 문자를 넣어보는 형태로 쉽게 체크할 수 있습니다.
Origin: hahwul.com (O)
Origin: google.com (X)
Origin: abcd.hahwul.com (O)
Origin: hahwulacom (O)
Origin: hahwulbcom (O)
Origin: abcdahahwul.com (O)
Origin: abcdbhahwul.com (O)