Computer Science/네트워크 및 웹 보안(Network & Web Security)

[네웹보/NWS] CSRF 공격

gxxgsta 2024. 4. 22. 17:30
반응형
SMALL

CSRF Attack

Cross-Site Requests and Its Problems

어떤 웹 페이지에서 다른 웹 페이지를 접속할 때, 원래 접속해있던 웹 페이지에서 새로운 웹 페이지에 대해 요청을 전송한다. 이때, 동일한 웹 페이지에 대해 요청을 날리는 경우를 Same-site request, 서로 다른 웹 페이지에 대해 요청을 날리는 경우를 Cross-site request라고 한다.

그런데 우리는 Cross-site request를 악용하여 공격을 수행할 수도 있다. 어떻게 가능한 것인지 알아보자.

 

우선 어떤 웹 페이지에서 다른 웹 페이지로 접속할 때를 가정했을 때, 어떤 웹 페이지를 A, 다른 웹 페이지를 B라고 하자.  A에서 B로의 접속 시 브라우저는 B와 관련된 쿠키 정보를 요청을 통해 B로 전송한다. 그런데, A와 B가 동일한 웹 페이지든, 서로 다른 웹 페이지든 쿠키가 담긴 요청은 항상 전송된다는 것이다.

 

그러나, 서버는 해당 요청이 동일한 페이지에서 온 건지, 서로 다른 페이지로부터 온 것인지 알지 못한다.

따라서 제 3의 사이트에서 동일한 웹 페이지에 대한 HTTP 요청을 훔치고, 훔친 요청을 원래의 웹 페이지에 실제로 보내지는 것과 동일한 HTTP 요청을 전송할 수 있게 된다. 이러한 해킹 방법을 우리는 Cross-Site Request Forgery (CSRF)라고 한다.

 

Cross-Site Request Forgery Attack

CSRF 공격을 위해 필요한 환경과 과정은 아래와 같다.

 

Environment Setup

- Target website

공격의 대상이 되는 웹 사이트이다.

 

- Victim user who has an active session on the target website

target website에서 사용이 가능한 활성 세션을 가지고 있는 피해 유저이다.

 

- Malicious website controlled

피해 유저의 쿠키 정보를 훔쳐 target webisite에 공격을 가할 수 있는 제어 가능한 악성 웹 사이트이다.

 

Steps

1. 공격자는 cross-site request를 수정하여 target website에 전송할 수 있는 악성 웹 페이지를 제작한다.

 

2. 공격자는 피해 유저가 악성 웹사이트를 방문하도록 유도한다.

 

3. 또한 피해 유저는 공격의 대상이 되는 웹 사이트에 대해 로그인이 되어 있어야 한다.

 

Environment Setup

 

우리는 웹 페이지에 대해 CSRF 보안이 설정되어 있지 않은 가상 환경에서 실습을 진행해야 한다. 최근 대부분의 사이트가 CSRF 공격에 대한 대비책을 구축하고 있기 때문이다.

 

또한 Elgg라는 social networking를 위한 오픈 소스 웹 어플리케이션을 이용하여 Apache’s Virtual Hosting으로 localhost로 호스팅 되고 있는 서버를 사용한다.

 

이때, target website는 http://www.csrflabelgg.com 이고, 공격하는 웹 사이트는 http://www.csrflabattacker.com 이다.

 

CSRF Attacks on HTTP Get Services

우리는 HTTP 요청이 제공하는 다양한 메소드 중 GET과 POST 요청을 공격에 사용할 예정이다. 따라서 각 요청들이 어떤 형식으로 전송되는지 살펴보자.

 

HTTP GET requests

GET 요청의 경우 웹 사이트가 처리하는 파라미터(foo, bar)가 URL 뒤에 붙어서 함께 날아가고 있는 모습을 보인다.

 

HTTP POST requests

POST 요청의 경우 웹 사이트가 처리하는 파라미터가 HTTP 요청의 데이터 필드, 즉 바디 내부에 위치해 있다.

 

CSRF Attack on GET Requests - Basic Idea

GET 요청을 통해 CSRF 공격을 할 때의 Basic idea에 대해 소개하겠다.

 

www.bank32.com 이라는 계좌 이체가 가능한 웹 사이트가 있다. 사용자는 웹 사이트에 로그인 되어 있으며, 인증된 사용자를 고유하게 식별하는 세션 쿠키(active cookie)를 가지고 있다. 사용자가 3220이라는 아이디를 가진 계좌에 500$를 송금할 때, HTTP 요청은 http://www.bank32.com/transfer.php?to=3220&amount=500와 같다.

 

이때, 공격을 수행하기 위해 공격자는 브라우저가 피해자의 세션 쿠키를 요청과 함께 전송할 수 있도록 요청을 위조하여 보내야 한다. 이때, 공격자는 이러한 요청 코드를 해킹 웹 페이지에서 Javascript 코드에 넣어 전송할 수 있다. 이때, 이러한 요청은 img나, iframe 태그의 src를 통해 진행할 수 있다.

각 태그에 사진과 같이 src에 요청 url을 넣어두면, 해당 서버에 url에 대한 요청이 날아간다. img 및 iframe과 같은 HTML 태그는 src 속성에 지정된 URL에 대한 GET 요청을 트리거할 수 있기 때문이다. 이 요청에 대한 응답은 image/website이다.

 

Attack on Elgg’s Add-Friend Service

이제, Elgg에서 강제로 친구를 추가하는 공격에 대해 알아보자.

 

우리의 목표는 피해자의 의지 없이 피해자의 친구 목록에 공격자를 추가하는 것이다.

 

공격자 Samy는 Elgg에서 Charlie라는 가짜 계정을 만든 후 자기 자신에게 친구 추가 요청을 보낸다. 이때, FireFox LiveHTTPHeaders 확장을 사용하여 친구 추가 HTTP 요청을 캡처하여 해당 웹 사이트가 어떻게 기능하는지 시스템 분석을 진행한다.

캡쳐된 HTTP Header의 모습은 위 사진과 같다. 하나씩 분석해보자.

 

- ①

Elgg의 친구 추가 요청 URL이다. 친구목록에 추가될 사용자의 UserID가 사용되며, 여기서 Samy의 UserID(GUID)는 42임을 확인할 수 있다.

 

- ②

Elgg가 채택한 CSRF에 대한 공격을 막기 위한 대응책이다. 서버가 날리는 secret token 값이다.

 

- ③

각 사용자에게 고유한 세션 쿠키 정보로 브라우저에서 자동으로 전송된다. 이때, 쿠키는 로그인된 피해 유저에 대한 쿠키 정보이다.

 

Create the malicious web page

해킹 페이지의 html의 구성이다.


img 태그는 HTTP GET 요청을 트리거한다. 브라우저가 웹페이지를 렌더링하고 img 태그의 src 속성에 지정된 URL로 HTTP GET 요청을 보낸다. 이때, 이러한 img 태그의 크기를 아주 작게 만들어 피해 유저가 눈치채지 못하도록 한다.

 

위와 같은 코드를 해킹 웹 사이트에 위치시켜 놓았을 때, 피해 유저가 로그인 된 상태에서 해당 웹 사이트에 접속하면 피해 유저의 브라우저에 해킹 웹 사이트가 로드되고, 위조된 친구 추가 요청이 Elgg 서버로 전송된다. 이때, 요청이 성공하면 피해 유저는 공격자에게 자동으로 친구 추가 요청이 전송되는 것이다.

 

CSRF Attacks on HTTP POST Services

이번에는 POST 요청을 통해 CSRF 공격을 수행하는 경우에 대해 알아보자.

HTTP request의 POST method는 파라미터가 GET과 달리 url에 존재하는 것이 아닌, body 내부에 위치하여 전송된다. 따라서 동일한 효과를 내기 위해 html의 form 태그를 사용한다. 

 

그러나 form 태그를 사용하면 사용자가 submit 버튼을 눌러야 서버로 요청이 전송된다. 공격자는 유저의 추가적인 클릭 없이 POST 요청이 전송되길 원하기 때문에 이러한 과정을 script로 구현할 수 있다.

앞서 form 태그의 형태를 script로 구현한 모습이다. 이때, forge_post() 함수를 통해 html의 form 태그를 구현해주고, submit까지 진행되도록 코드를 구성한다. 이후 웹 페이지가 로딩되자마자 해당 함수가 실행되도록 코드를 구성해주면 유저가 해킹 웹 사이트에 방문할 때 바로 POST 요청을 전송할 수 있다.

 

 Attack on Elgg’s Edit-Profile Service

이번에는 Elgg에서 프로필을 자동으로 업데이트 하도록 하는 공격을 해보자.

 

우리의 목표는 피해 유저의 동의 없이 피해 유저의 프로필에 “SAMY is MY HERO”라는 문구를 넣는 것이다. 이를 위해서 앞서 GET 공격과 마찬가지로 프로필을 수정할 때의 POST 요청을 LiveHTTPHeader 확장으로 캡쳐하여 해당 웹 사이트가 어떻게 기능하는지 시스템 분석을 진행한다.

위 사진은 공격자인 Samy가 자신의 프로필을 수정하려고 했을 때의 POST 요청을 캡처한 모습이다.

 

-

edit 관련 요청을 처리하는 서비스 서버의 위치이다.

 

-

① 이후부터 세션에 대한 정보를 포함하며, ②는 세션 쿠키에 대한 정보이다. 브라우저에서 자동으로 발급해주며, 각 유저 별로 고유한 값을 가진다.

 

-

secret token에 대한 값으로, 이후에 다시 설명하도록 하겠다.

 

-

실제로 프로필에서 update 하는 내용이다.

 

-

소셜 네트워크에서 정의한 부분으로 필드에 대해 접근하는 level을 지정할 수 있는 부분이다. 2는 전체 공개를 뜻한다.

 

-

피해자의 uid가 들어가는 부분이다. 위의 캡쳐에서는 Samy가 프로필 수정을 진행하였기 때문에 해당 부분에 특정 유저의 uid를 얻어 공격을 할 수 있다. 이때, 아래의 사진처럼 대상 웹 페이지에서 코드를 확인하며 uid를 얻을 수도 있다.

 

Craft the Malicious Web Page

앞서 분석한 POST 요청을 통해 위와 같이 공격 페이지를 구성할 수 있다. html의 form 태그를 script로 구성한 것과 같이 코드를 구성해주면 된다. 피해 유저는 해당 웹 페이지를 방문하면 자동으로 프로필이 수정될 것이다.

 

Fundamental Causes of CSRF

CSRF가 발생하는 근본적인 이유는 서버가 요청을 받았을 때, 해당 요청이 same-site에서 온 것인지, cross-site에서 온 것인지 구분하지 않는다는 것이다. 이때, 우리는 same-site의 경우 신뢰할 수 있지만, cross-site의 경우 신뢰할 수 없기 때문에 두 경우를 다르게 처리해주어야 한다.

 

그러면, 브라우저는 요청이 어디서 온 것인지 파악할 수 있을까? 당연히 가능하다. 왜냐하면 어떤 웹 사이트를 방문할 때 브라우저가 쿠키에 대한 정보를 저장하고 있다가, 쿠키를 웹 사이트에 전송해주기 때문이다.

 

따라서, 브라우저는 포함하여 서버가 요청을 처리하는 세 가지 방법이 존재한다.

- Referer header (브라우저)
- Same-site cookie (브라우저)
- Secret token (서버가 CSRF로부터 자신을 지키기 위함)

 

이때, 괄호 안에 적힌 것은 해당 요청을 구분하거나 처리할 수 있는 주체를 이야기한다.

 

Countermeasures: Referer Header

HTTP header field에는 해당 요청이 생성된 위치에 대한 웹 페이지 주소를 식별할 수 있는 Referer Header가 존재한다. 따라서 서버는 해당 필드의 값을 통해 서버와 동일한지 판단할 수 있다.

 

그러나 이러한 방법에는 두 가지 문제점이 존재한다.

 

privacy concern

네이버 뉴스와 같은 사이트의 경우 다른 뉴스 사이트에서 페이지를 가져오는 경우가 많다. 다른 웹 사이트를 방문할 때마다 요청의 헤더에는 해당 요청이 어디에서 발생됐는지에 대한 정보가 있다. 따라서 서버는 특정 사용자에 대해 어떤 사이트를 방문했는지 모두 알 수 있기 때문에, privacy concern 문제가 발생한다.

 

unreliable

또한, 서버도 해당 정보를 신뢰할 수 없다.

 

Countermeasures: Same-Site Cookies

CSRF가 발생하는 이유는 요청이 날아가며 쿠키에 세션 정보가 저장되어 있기 때문이다. 이때, 쿠키는 서버에서 클라이언트로 발급하므로 서버는 암호 통신(https) 시에만 쿠키를 제공하도록 브라우저에 요청할 수 있다.

 

Chrome이나 Opera와 같은 브라우저는 속성을 부여할 수 있는 SameSite라는 쿠키를 제공한다. 이 쿠키는 서버가 cross-site request시 쿠키의 전송 여부를 설정할 수 있다. 이러한 속성을 가진 쿠키는 same-site request에 대해서는 항상 쿠키를 전송하지만 cross-site request에 대해서 쿠키의 전송 여부에 따라 속성값이 달라진다.

 

속성값은 두 가지가 존재한다.

- Strict (모든 cross-site request에 대해 쿠키를 전송하지 않음)

- Lax (조건이 맞는 cross-site request에 대해서만 쿠키를 전송)

 

이러한 쿠키는 쿠키 자체를 보호할 수 있는 정책이다.

 

Countermeasures: Secret Token

서버에서 무작위로 Secret Token을 생성하여 클라이언트에 제공한 후, 클라이언트가 요청을 전송할 때 항상 첨부하도록 하여 이전의 발급한 Secret Token의 값과 동일한지 비교하도록 하는 방법이다. Secret Token 사용 시 공격자는 Secret Token 값이 무엇인지 미리 알 수 없기 때문에 공격 웹 사이트 제작이 어렵다.

 

서버는 Secret Token 값을 이용하여 해당 요청이 동일한 웹 페이지에서 발생한 것인지 판단할 수 있다. 다른 웹 페이지에서 발생한 요청의 경우 Secret Token 값에 접근을 할 수 없기 때문이다. 이러한 정책을 Same Origin Policy(SOP)라고 하는데, 이 정책은 브라우저에서 지원해준다.

 

Secret Token은 유저 별로 서로 다른 값을 할당해야 한다. 모든 유저에 대해 동일한 Secret Token를 사용하는 경우 Secret Token을 한 번만 알아내면 여러 번 재사용할 수 있기 때문이다. 따라서 재사용 공격을 방지하기 위해 session ID와 같은 값을 이용하여 Secret Token을 생성해야 한다.

 

Elgg’s Countermeasure

Elgg에서 채택한 방법은 Secret Token이다. 이때, Secret Token은 _elgg_tc와 _elgg_token이며, 두 개의 값들은 java script 코드 내부 및 유저의 action이 적용되는 곳에 저장된다.

 

이 값들은 서버에서 생성한 랜덤 값으로 정의가 되고, 유저가 request를 보낼 때마다 두 Secret Token 값이 요청에 포함되도록 두 개의 숨겨진 매개변수가 추가된다. 이때, 모든 request마다 Secret Token 값이 함께 전송되므로 공격자는 이 값을 미리 예측할 수 없다.

 

Elgg에서 Secret Token은 4개의 정보를 MD5로 해시한 값을 사용하며, 사용하는 정보는 아래와 같다.

- Site secret value
- Timestamp
- User session ID
- Randomly generated session string

 

Bypassing Secret Token

우리는 어떠한 조건이 존재하면, Secret Token을 우회할 수 있다. 이러한 조건에 대해 알아보자.

 

1. the target page does not set X-FRAME-OPTIONS headers

우리의 공격 대상이 되는 웹 페이지가 X-FRAME-OPTIONS 헤더를 설정하지 않아야 한다. X-FRAME-OPTIONS을 설정하면 내가 만든 페이지에서 다른 페이지로 들어갈 수 있는지에 대한 여부를 결정할 수 있다.

X-FRAME-OPTIONS은 두 가지 버전으로 설정할 수 있는데,

DENY은 다른 프레임으로의 접근을 아예 차단하는 것이고,

SAMEORIGIN은 동일한 출처, 즉 같은 도메인으로의 임베딩만 가능하도록 하는 것이다.

 

2. Without X-FRAME-OPTIONS, clickjacking attack can be performed

X-FRAME-OPTION이 없으면 clickjacking 공격이 수행될 수 있다. clickjacking은 후에 더 다룰 것이지만 간단한 시나리오와 함께 알아보도록 하자.

 

시나리오는 다음과 같다.

1. 모든 요청마다 secret token이 함께 전송된다.

 

2. secret 창을 비워둔 채로 요청을 전송해본다.

 

3. 웹 사이트에 따라 실제 서버에서는 업데이트 되지 않지만, 브라우저 단에서 업데이트 될 수 있다. (예를 들어 브라우저가 오류 메세지를 전송하는 것) 이때, 요청을 resubmit하라고 할 수 있다.

 

4. 이때, resubmit 시 토큰값이 제대로 들어간다면 반영된다. 따라서 우리는 투명한 resubmit 버튼을 만들어 사용자가 클릭할만한 곳에 위치시킨 뒤 resubmit 버튼을 클릭하도록 유도한다. 클릭한다면 해킹에 성공하는 것이다.

 

앞서 언급한 시나리오 외에도 다양하게 secret token을 우회할 수 있는 방법이 존재한다. 예를 들어 POST 요청을 GET 요청으로 바꾸어본다던가, 값이 없는 경우에 대한 예외처리를 하지 않은 웹 사이트의 경우 token parameter를 빈 값으로도 전송해볼 수 있다.

 

https://gowthams.gitbook.io/bughunter-handbook/list-of-vulnerabilities-bugs/csrf

위 사이트에 더욱 많은 시크릿 토큰 우회 방법이 기술되어 있다.

 

Handling JSON content-type

application/json의 Content type은 보통 AJAX를 사용하여 전송된다. 이때, 전송되는 형식은 위와 같으며 데이터는 key-value 형태로 전송된다. 

 

이때, 마찬가지로 CORS 세팅이 가능한데, 아래 두 가지 정책을 소개하겠다.

 

Access-Control-Allow-Origin (ACAO)

same site or cross site의 request 전송에 대한 정책을 설정할 수 있다.

 

Access-Control-Allow-Credentials (ACAC)

사용자의 인증 정보(쿠키, SSL 등)가 함께 전송 가능하지에 대한 정책을 설정할 수 있다.

 

위와 같은 정책에 제대로 설정되어 있는 경우 우리는 json의 형식으로 CSRF 공격을 수행할 수 없다. 하지만 json의 형식이 아닌 다른 형식으로 공격을 수행할 수도 있다.

 

1. Try to send the request with different types

첫 번째 방법은 Content-Type을 변경하는 방법이다. 좌측이 변경 전이고, 우측이 변경 후 모습이다. Ajax(json) 형태의 메세지를 정책으로 인해 전송이 불가능할 때, text/plain 형식은 form 형태로 데이터를 전송할 수 있다. form인 경우 resource sharing permission의 적용을 받지 않을 수 있다.

 

text/plain 형식으로 데이터를 전송하려고 하는데, 좌측의 json 형식에서 "foo=": "bar"이라고 하는 dummy data를 추가하는 모습을 볼 수 있다. 해당 부분은 form의 데이터 전송 방식에 의해 필요하다.

 

form의 데이터 전송은 name = "name" value = "value" 형식으로 전송되며, 서버에는 "name" = "value" 형식으로 전송된다. 이때, "name"과 "value" 사이에 등호(=)가 새롭게 생기게 된다. 따라서 해당 형식을 맞춰주기 위해 "foo="라는 이름을 가진 dummy data를 삽입해 준 것이다.

 

2. Check if CORS is miss configured

1번의 방법으로 공격이 실패했을 때, 개발자가 보안 정책 설정 시 어떤 실수를 하지 않았는지 확인한다.

위의 사진에서는 요청 시 내 origin을 넣고서 request를 전송한 모습이다. 이때, ACAC가 true로 뜨는데, 이 의미는 attacker.com에서 cross-site request를 보내도 받는 쪽에서 쿠키를 전송할 수 있다는 의미이다.

 

resource sharing는 서버의 작업, 즉 개발자의 역량이기 때문에 개발자의 실수로 해당 부분에 문제가 발생할 경우 이러한 공격 방법을 사용할 수 있다.

반응형
LIST