모의해킹 스터디 복습

모의해킹 스터디 12주차: CSRF (사이트 간 요청 위조)

whydontyoushovel 2025. 1. 14. 06:20

 

 

 

 

 

 CSRF 

Cross-Site Request Forgery

 

피해자가 자신의 의도와 무관하게 서버로 어떤 요청을 하게 만드는 공격.

 

 

실생활 예시)

은행에 필요한 서류를 제출했더니 통장 잔고가 0원이 되어있음.
서류 중에 잔고를 모두 인출하겠다는 내용이 숨겨있던 것.
=> 사용자가 의도하지 않은 요청을 교묘히 숨겨 공격자의 목적을 달성시킴.

 

 

공격 예시)

공격자의 목표:
피해자가 본인도 모르게 마이페이지에 접속해서 비밀번호를 1234로 변경하게 만들자.

절차
1) 만들어둔 계정으로 마이페이지에서 비밀번호를 변경해봄.
2) 버프스위트 등을 이용해 해당 요청 확인
3) 만약 비번 변경 시 기존 비번 등의 인증 데이터가 따로 필요 없고, GET방식으로 요청이 가능하다면
4) 필요한 파라미터가 포함된 링크를 피해자가 누르게 만듦 (https://example.com/mypage_change.php?pass=1234&submit=submit 등)
5) 피해자가 링크를 누르면 페이지 요청하면서 비밀번호 변경됨.

 

 

 

 

 CSRF 발생 포인트         

"""요청""" 위조

 

▷ 발생 포인트: 임의로 만들 수 있는 모든 요청

사용자의 요청을 위조하는 것이기 때문에 공격자가 임의로 만들 수 있는 모든 요청에서 CSRF가 일어날 수 있음.

예를 들어 유튜브 url로 비디오를 요청할 수 있다면 여기서도 CSRF가 일어날 수는 있음.

 

 

▷ 위험도: 컨설턴트 주관에 따름

컨설턴트 주관에 따라 취약점인지를 판단함.

모의해킹 컨설턴트가 url링크로 유튜브 비디오를 볼 수 있는 게 위험하다고 판단했다면(공격자가 써먹을 수 있겠구나 싶으면) 취약점으로 간주할 수 있음.

(※ 타당한 근거 필요)

 

 

▷ 보편적으로 위험한 위치면 잡아

개인정보 변경, 관리자를 노린 게시판 글 작성 등 누가봐도 위험하다고 판단되는 위치에서 발생한 CSRF는 잡아야함.

 

 

인증 정보 없이 요청을 보내는 곳이면 CSRF 다 잡을 수 있음

~ 컨설턴트에 따라 덜 위험해보일 수 있음 ~

 

***이렇게 자잘한 게 많아서 CSRF를 취급하지 않는 컨설턴트도 종종 볼 수 있음.

즉, 취약점이 꽤 많이 남아있다는 뜻. 대신 CSRF 발견하면 XSS를 쥐잡듯이 잡아서 같이 묶어 보고하는 게 보기에 좋다~

 

 

 

 

 XSS와의 접점           

CSRF는 XSS와 엄연히 다른 공격이지만 이 둘을 합치면 공격 방법 다채로워지고 Zero Click 공격이 가능해짐.

=> 매우 위험

 

***Zero Click 공격:

더보기

링크를 클릭하거나 DOM요소에 마우스를 올려두는 등의 사용자 상호작용 없이도 이루어지는 공격.

CSRF + XSS공격의 경우 게시물을 읽는 것만으로 사용자가 의도하지 않은 요청이 서버에 전달될 수 있음.

 

>>예시 코드

<img src="http://example.com/mypage_change.php?password_new=1234&Change=Change">

 

  공격자가 위와 같은 이미지 태그를 XSS취약점이 있는 페이지에 작성하면 사용자가 해당 페이지를 읽으려고만 해도 공격자가 의도한 요청을 저도 모르게 서버에 전송하게 됨. 

 

 

  • GET요청 못 쓰게 POST방식으로 바꾸면 되지 않나?

  XSS가 발견될 가능성이 있는 한 POST방식으로 요청을 변경하는 건 대응 방안이 될 수 없음. (링크를 쓰지 않아도 POST방식으로 공격 가능. 아래 <POST방식에서 CSRF 공격하기> 참고.)

  CSRF 공격을 예방하고 싶다면 요청에 인증 정보를 포함시키는 것이 베스트.

 

 

  • XSS 취약점이 없으면 CSRF 취약점도 없나?

XSS 취약점이 없다:

사용자가 임의로 스크립트를 삽입할 수 없음.

 

스크립트 없이 CSRF 공격이 가능한가:

가능. 링크 등을 사용할 수 있음. 즉, XSS 취약점 없이 CSRF 공격 가능.

다만 XSS 취약점이 있으면 다양한 CSRF 공격 페이로드를 짤 수 있음. 날개를 달아준다는 뜻.

  XSS CSRF
공통점 클라이언트 측 공격 (피해자가 사용자)
차이점 클라이언트 측
스크립트 필요 여부
자바스크립트 등 필요 스크립트 없어도 링크 등을 통해 공격 가능
공격자의 의도를
수행하는 주체
사용자 브라우저가 스크립트를 처리 서버가 요청 처리

 

둘은 엄연히 별개의 공격임.

 

 

 

    + XSS로 할 수 있는 공격 +

 

1. 페이지 리다이렉션

2. 쿠키 탈취

3. 키로거

4. HTML Overwrite

5. 데이터 탈취

 

(CSRF취약점이 있으면)

6. CSRF  ← new! 

 

 

 

 

 POST 방식에서 CSRF 공격하기          

폼 태그 활용

 

XSS가 발견될 가능성이 있는 한 POST방식으로 요청을 변경하는 건 대응 방안이 될 수 없음.

=> 스크립트를 삽입하면 POST로 CSRF 가능.

=> 폼 태그를 이용해 POST방식으로 요청을 보낼 수 있음.

=> 아래의 과정을 거쳐 스크립트가 작성된 게시글을 읽는 것만으로도 피해자 모르게 요청을 보낼 수 있게 됨.(제로클릭 공격)

 

  1. POST요청으로 CSRF하기: form태그
  2. 제출 버튼 없이 폼을 제출하기:  submit 자동화
  3. 폼 제출 시 페이지 이동을 없애기: iframe
  4. iframe 안보이게 하기: style 속성

 

포트 스위거 아카데미에서 실습

https://portswigger.net/web-security/csrf/lab-no-defenses

 

 

▷ 탐색하기

my account 페이지 요청

로그인 페이지 리다이렉션
제공된 계정으로 로그인하여 접속

my account 페이지 접속
여기서 내 계정의 이메일을 수정할 수 있음.

 

동작을 확인하기 위해 임의의 이메일을 입력한 뒤
update email 버튼을 클릭

나의 이메일 정보가 wiener@normal_user.net에서 
hahaha@test.com으로 바뀌는 것을 확인

 

 버프 스위트로 이메일 변경 시 발생한 요청 중
맨 위 change-email의 요청을 확인

POST방식으로 내가 입력한 이메일을 전달하고 있음.
이 과정에서 인증 정보를 필요로하고 있지 않으므로 CSRF가 일어날 가능성이 있다고 판단됨.
X-Frame-Options: SAMEORIGIN -> POST요청에서 CSRF 공격을 하기 위해 필요한 iframe도 같은 도메인에서 접근할 수 있음.

 

 

 

 

▷ form 태그로 POST 요청 보내기

공격 시뮬레이션을 위해 페이지 상단 Go to exploit server 클릭

 

요청 데이터를 만들 수 있는 페이지에 접속함

<!--post방식으로 my-account디렉토리의 change-email파일에 파라미터 전달-->
<form method="POST" action="https://0a5c005b04807dfb824b3e0a0009002f.web-security-academy.net/my-account/change-email">
  <!--type속성을 hidden으로 설정해서 인풋박스가 보이지 않게 함-->
  <input type="hidden" name="email" value="formtag@test.com"/>
  <input type="submit" value="click me"/>
</form>
요청 데이터의 본문에 이메일 변경을 요청할 form태그를 작성함.
작성 후 동작을 확인하기 위해 페이지 하단의 view exploit 클릭.

submit 버튼이 생성된 페이지가 출력됨.
form@test.com 데이터를 이메일 변경 페이지에 전송하기 위해 클릭.

my account 페이지로 리다이렉션됨.
해당 페이지에서 의도한 이메일로 수정된 것을 확인
CSRF 공격이 정상적으로 수행됨.

 

submit 버튼 클릭 시 요청한 데이터 확인.
요청 본문에 의도한 파라미터가 포함되었음.
정상적으로 처리되었기 때문에 /my-account?id=wiener 페이지로 리다이렉션 됨.

 

해당 페이지는 사용자 입력값이 단순 문자열로 취급되지 않고 스크립트로서 렌더링되고 있음.

즉, XSS 취약점이 동시에 발견되고 있으므로 사용자가 페이지를 읽는 것만으로도 이메일을 수정하도록 만들 수 있음.

 

제로 클릭으로 진화!!!

 

 

▷ submit 자동화

자바스크립트 등장

<h1>hello!</h1>
<form method="POST" action="https://0a5c005b04807dfb824b3e0a0009002f.web-security-academy.net/my-account/change-email" id="myForm">
  <input type="hidden" name="email" value="formauto@test.com"/>
</form> <!--submit버튼 삭제-->
<script>
  document.getElementById('myForm').submit();
</script>
form 태그에 id 속성을 부여하고 스크립트로 DOM객체에 접근
submit 메소드로 자동 제출하도록 만듦
동작 확인을 위해 view exploit 클릭

 

 

숨겨진 폼이 표시된 페이지 생성 (hello!)
아무 동작 없이 my account 페이지로 리다이렉션 됨.
input의 value속성으로 지정한 이메일로 계정 정보가 바뀜.

 

버프 스위트를 통해서도 스크립트가 정상 작동하였음을 볼 수 있음.

 

이렇게 하면 사용자 상호작용 없이 CSRF 공격으로 계정 정보를 바꿀 수 있지만 원치 않게 마이페이지로 리다이렉션되어 피해자 입장에서 금방 이상을 눈치챌 가능성이 매우 높음. (나같아도 그럼)

이것도 보완할 방법이 있음. iframe 태그를 사용하면 됨.

 

노 리다이렉션으로 진화!!!!

 

 

 

▷ iframe으로 페이지 이동 없애기

form 전송 결과를 iframe에 표시하기

<h1>hello!</h1>
<iframe name="frame"></iframe>
<form method="POST" action="https://0a5c005b04807dfb824b3e0a0009002f.web-security-academy.net/my-account/change-email" id="myForm" target="frame">
  <input type="hidden" name="email" value="iiframe@test.com"/>
</form>
<script>
  document.getElementById('myForm').submit();
</script>
form태그의 target 속성을 이용해 iframe의 name속성에 부여한 값을 입력하면
(iframe: name="frame" / form: target="frame")
form 의 데이터를 전송한 결과를 iframe 화면에 출력시킬 수 있음.
정상적으로 실행된다면 view exploit 버튼을 누른 후
hello!가 표시된 페이지에 iframe 창이 뜬 것을 볼 수 있을 것.

 

 

작성한 html 페이지가 나타난 뒤 페이지 리다이렉션은 일어나지 않음.
요청이 전송되어 이메일 변경이 완료되었는지 보기 위해 버프스위트를 확인.

이메일 변경 요청 데이터와 이메일 변경 완료 후 my account 페이지가 요청된 흔적을 볼 수 있음
변경 요청 시 iiframe@test.com을 파라미터로 가져가서 정상 작동 후 my account 페이지로 리다이렉션함.
요청된 my account 페이지에서 변경된 이메일을 확인할 수 있었음.

하지만 이게 끝이라면 click me 버튼이 있었을 때와 다를 바가 없음. iframe이 화면에 보이기 때문임.

페이지 리다이렉션되었을 때보다는 덜할 것 같지만 사용자는 저 수상한 상자는 무엇인고..하며 의심할 것임.

그래서 마지막으로 iframe 객체가 화면에 출력되지 않게 해야함.

 

의심할만한 것들 다 숨김!!!!

 

 

 

▷ style 속성으로 iframe 안 보이게 하기

display: none

<!--style 속성으로 iframe 숨기기-->
<iframe name="Frame" style="display: none;"></iframe>
<form method="POST" action="https://0adc00080384fd698660a5fc00f90072.web-security-academy.net/my-account/change-email" id="myForm" target="Frame">
  <input type="hidden" name="email" value="noshowframe@test.com"/>
</form>
<script>
  document.getElementById('myForm').submit();
</script>
iframe 태그의 style 속성으로 css처럼 요소를 원하는 모습으로 바꿈.
여기서는 iframe 요소가 보이지 않아야하기 때문에 display: none으로 설정함.
굳이 필요없는 h1 태그도 지워줌.
의도한 바를 수행하는지 확인하기 위해 view exploit을 클릭함
정상 작동할 경우 비어있는 페이지가 뜬 채로 아무 표시 없이
이메일이 noshowframe@test.com으로 변경될 것

 

 

view exploit을 클릭하자 위와 같은 화면에서 변화가 보이지 않음.
데이터가 잘 전달되어 이메일이 변경되었는지 확인하기 위해 버프 스위트로 이동

noshowframe@test.com을 파라미터로 가져가
이메일 변경 페이지에서 302 응답을 받음.
리다이렉션된 my account 페이지에서 변경된 이메일 확인.
스크립트 정상 작동하여 사용자의 요청을 위조할 수 있었음.

 

  • 위와 같이 POST 방식에서도 눈 뜨고 코 베일 수 있으니 메소드 변경은 CSRF에 대한 대응법으로 추천할 것이 못됨.
  • 앞서 밝혔듯이 CSRF 공격에 대한 대응으로 가장 적절한 것은 요청에 인증 정보를 포함시키는 법임.
  • 다만 스크립트가 필요하기 때문에 사이트에 XSS 취약점이 전혀 없다는 전제하에는 적용할만함. (그게 더 쉽고 효율적일지는 사이트 특성을 잘 고려해야함.)

 

POST로 CSRF 가능!!!

 

 

 CSRF Token      

그래서 인증 정보가 뭔데?? 

 

요청에 포함시킬 수 있는 인증 정보는 다양함.

개인정보를 변경하기 위해 기존 비밀번호를 입력할 때나, 사용자별로 고유한 데이터를 발급하여 어떤 요청 시 꼭 그 데이터가 포함되게 할 때 등이 그러함.

 

CSRF Token은 후자에 해당함.

 

▷ CSRF Token

  • CSRF 공격을 막기위해 도입한 랜덤한 토큰값 (예시: aweAWER#$@#234234AW#%#5%23%AWD)
  • 인증 정보 역할을 함.
  • 방식:

   마이페이지나 글쓰기 페이지의 form 태그에 접근할 때 토큰 생성 -> 세션 저장 -> 사용자에게 발급

요청 시 파라미터에 함께 보내게 함.

<input type="hidden" name="csrfToken" value="wer@#%wtq#%32qA3@%"/>
  • mypage.php?id=haha&info=&pw=1234&csrfToken=wer@#%wtq#%32qA3@%
  • 요청 제출 시 세션에 저장된 토큰과 비교
  • 일치하면 요청 정상 처리
  • 일치하지 않으면 처리X

 

근데 이것도 경우에 따라 우회됨!!!

CTF 과제에서 계속...

 

 

 

▷ JWT에선 CSRF Token를 어떻게 다룰까?

(※ 주의: 정답 정리 X, 찾은 내용 기록 O)

 

  • 의문:

JWT는 쿠키에 저장함. 세션은 굳이 필요하지 않을 것 같은데 CSRF Token을 위해 일부러 세션을 적용해야할까?

  • CSRF Token의 필요성:

JWT는 사용자 로그인 정보를 포함하고 있음. 로그인 유지하려면 모든 요청에 포함시켜야함할 때만 필요한 것이 아니므로 CSRF Token을 대체할 수 없음.

  •  요구사항:

CSRF Token + JWT + XSS대응(httponly 등)

  • 요점:

1) JWT와 CSRT Token을 저장하는 위치

2) 세션으로 처리하느냐, JWT에 포함시키느냐

에 따라 CSRF Token 다루는 법이 달라짐.

  • 결론:

Q) JWT는 세션이 굳이 필요하지 않을 것 같은데 CSRF Token을 위해 일부러 세션을 적용해야할까?

→ CSRF Token이 서버에서 발급한 것이 맞는지 비교하려면 서버에서 같은 데이터를 가지고 비교해야한다고 생각했는데 JWT에 포함시켜서 확인하는 방법도 있는 것 같음.

  1. 세션을 이용할 경우
  2. JWT에 포함시킬 경우

1번의 경우 토큰 사용 시 코드가 간결해지겠지만 세션은 stateful, jwt는 stateless이므로 상태가 부닥칠 가능성이 있다고 함. (불가능한 건 아닌 듯.) 그리고 로그인, 로그아웃 등의 기능에서 둘다 처리해야함..

2번의 경우 상태 부닥칠 걱정이 없어 서버가 튈 위험이 적어지겠지만 JWT에서 CSRF Token값 빼서 비교하는 로직 필요.

 

XSS로 토큰에 접근하는 걸 막으려면 세션 스토리지나 로컬 스토리지에 저장하는 것보다 쿠키에 저장해서 httponly로 막는 게 나을 것 같음.

뭐가 됐든 XSS가 뻥뻥 뚫려있으면 CSRF 위험도가 증가함.

 

내가 JWT를 구현한다면 JWT에 포함시키고 쿠키에 저장해서 httponly 쓰려고 할 것 같다..