728x90

정의

사이트간 요청 위조

피싱을 활용해 사용자 모르게 패스워드 변경할 수 있다.

공격자가 보낸 이메일 링크를 클릭 시 사용자 모르게 패스워드 변경이 발생한다.

단, 사용자는 링크 클릭 시 패스워드 변경이 일어날 웹 사이트에 로그인되어 있어야한다.

 

실습

Low 단계

CSRF 페이지에 접속 후 abcd로 비밀번호 변경을 요청하고  Burp Suite로 메시지를 보면 다음과 같다.

만약 사용자가 어떤 링크를 클릭 시 해당 메시지가 전송된다면 비밀번호가 변경될 것이다. 단, 사용자는 해당 웹 사이트에 로그인되어 있어야한다. 왜냐하면 웹 서버는 세션쿠키 값으로 사용자를 식별하기 때문이다. 따라서 비밀번호 변경 요청 시 세션쿠키 값은 로그인되어 있어야만 전송되고 위 이미지에선 헤더의 Cookie에 해당한다.

https://raw.githubusercontent.com/SecuAcademy/webhacking/master/csrf.html

링크에 들어가면 csrf 공격 스크립트가 있다. wget 명령으로 다운받는다. 그리고 dvwa의 htdocs 디렉토리 밑으로 옮긴다. 그래야만 url을 통해 접근가능하다.

코드를 간단히 설명하자면 자바스크립트의 ajax 기법을 이용해서 기존의 비밀번호 변경 요청 메시지와 동일하게 URL과 파라미터들을 구성한다. 단, 패스워드는 hacker로 변경한다. 링크를 누르면 poc 함수가 실행되어 비밀번호 변경이 발생한다.

피싱을 위한 이메일을 작성한다. click here 단어에 csrf.html 파일의 링크를 삽입 후 전송한다.

메일을 열고 click here을 누르면 csrf.html 파일이 열린다.

실습을 위해 한번 더 클릭을 하지만 실제론 CLICK을 누른 순간 공격이 실행될 수 있다.

실습에선 확인하기 위해 Done!! 알림창이 뜨도록 했다.

비밀번호가 hacker로 바꼈는지 확인하기 위해 다시 로그인한다.

여기까지 따라했다면 정상적으로 비밀번호가 변경되었을 것이다.

Brup Suite의 Comparer 기능을 이용해 정상적인 비밀번호 변경 요청과 CSRF를 이용한 비밀번호 변경 요청을 보면 형태가 유사한 것을 알 수 있다. 주목할 점은 세션 쿠키의 값이 동일하다는 것이다.

Medium 단계

Medium 단계에서도 Low 단계와 동일한 방식으로 비밀번호 변경이 가능하다.

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords match?
        if( $pass_new == $pass_conf ) { 
        ...

stripos 함수를 사용하여 헤더의 Referer에 SERVER_NAME(도메인명) 문자열이 존재하는지 확인하고 있다. Referer은 바로 이전의 URL이다. Low 단계 마지막 이미지의 왼쪽(CSRF공격) 부분의 Referer을 참고하자.

현재 로컬에서 공격자와 희생자 둘 다 실행하므로 도메인명인 localhost가 Referer에도 존재하기에 통과한다. 하지만 위의 조건문의 의도는 다른 사이트에서 보낸 요청인지를 확인하여 같은 사이트인 경우에 비밀번호를 변경하는 것이다. 해커의 사이트였다면 필터링되었을 것이다.

만약 CSRF 공격이 해커 사이트가 아닌 웹 서버에서 실행된다면 위의 조건문은 우회가능하다. 따라서 csrf.html 파일에서 자바스크립트를 사용했기에 같은 웹 사이트 내의 웹 페이지에 XSS 취약점이 있다면 스크립트문을 실행하여 CSRF 공격을 수행할 수 있다.

위의 방식은 문제점이 또 존재한다. Referer에 도메인명이 존재하는지 확인하기 때문에 파일명에 도메인명을 넣는다면 우회가능하다. 현재 로컬에서 테스트하고 있기에 도메인명은 localhost이다. 파일명이 localhost를 포함하도록 만든다.

Referer에 localhost가 존재한다. 마찬가지로 CSRF 공격이 성공한다.

High 단계

Medium 단계의 방식이 통하지 않는다. 정상적인 요청을 보자.

못 보던 user_token이 생겼다. CSRF 토큰이다. CSRF 토큰은 CSRF 실습 페이지(localhost/dvwa/vulnerabilities/csrf)에 접근하면 서버에서 발급해주는 랜덤의 토큰이다. 이 토큰을 가지고 비밀번호 변경을 요청해야 서버는 정상적인 요청으로  받아들인다. 피싱을 통한 비밀번호 변경 요청이 실패했던 이유다.

이를 우회하기 위해서 토큰을 발급받고 이어서 바로 토큰을 가지고 비밀번호 변경 요청을 하는 코드를 이용한다. 마찬가지로 자바스크립트의 ajax 기법이다.

https://raw.githubusercontent.com/SecuAcademy/webhacking/master/csrfhigh.js

wget 명령어로 다운받은 후 /opt/lampp/htdocs 하위에 옮긴다.

코드 내용을 살펴보자.

req1 함수는 dvwa의 CSRF 페이지에 접속하는 것과 동일한 요청이다.

req2 함수는 CSRF 토큰을 추출하여 패스워드 변경 요청을 보낸다.

java script 코드이기 때문에 js 파일을 실행가능한 XSS (Stored) 페이지에 접속한다.

대신 High 단계에선 스크립트 태그의 입력이 막혀있기 때문에 편의상 Low 단계로 변경한다.

다시 High 단계로 변경 후 XSS (Stored) 페이지에 접속한다.

추출한 CSRF 토큰이 보이는 걸로 보아 공격이 성공했음을 알 수 있다.

다시 로그인 해보면 패스워드가 hacker로 변경되었다.

대응 방안

Impossible 단계를 보면 Current password를 입력해야 패스워드 변경 요청을 할 수 있다. 다만, 비밀번호를 매번 입력하기 번거로운 경우 Referer 헤더나 CSRF 토큰을 이용할 수 있다. CSRF 토큰 구현은 개발 언어나 개발 프레임워크에 따라서 기본 라이브러리나 플러그인 또는 설정의 형태로 지원한다.

이외에도 CAPTCHA를 이용하여 사용자의 직접적인 요청인지 검사하는 방법이 있다.

 

 

728x90

'dvwa' 카테고리의 다른 글

SQL Injection  (0) 2023.05.16
File Upload  (0) 2023.05.06
File Inclusion  (0) 2023.05.06
Command Injection  (0) 2023.05.02
Brute Force  (0) 2023.05.02

+ Recent posts