[ SQLi ] Find SQL Injection point

d4r6j·2025년 6월 10일

hack

목록 보기
7/11
post-thumbnail

Review

  1. Union
  2. Error Based
  3. Blind
  • 3 가지 모두 우리가 원하는 SELECT 문을 실행하는 것.
  • 어떤 SELECT 문을 실행할 지, 실행하고 싶은 SELECT 문이 무엇인지 명확해야 한다.
  • database, table, column 이름을 아는 작업을 진행 했다.

이 SELECT 문을 어떻게 Union, Error Based, Blind 에 끼어 넣어서 추출하느냐, 그것에 따라서 3 가지 유형으로 나누어져 있었다.

  • SQL Injection 이 일어나는 곳에서는 Blind 가 모두 다 가능하다.
    • 하지만 한 글자씩 찾아야 하는 overhead 가 걸리는 단점이 존재.
  • Blind 보다 상황에 맞춰서
    1. SQL 질의문 결과가 화면에 출력되는 경우 : UNION SQL
    2. SQL 에러 메시지가 화면에 출력되는 경우 : Error Based SQLi
    3. SQL 질의문 결과가 화면에 안나오는 경우 : Bllind SQL Injection (만능)

SQL Injection Point 란?

SQL Injection Point 는 어디서 발생할까?

  • DB 에게 SQL Query 를 사용하는 곳 ( data, parameter 포함하여. )
  • SQL Query 질의문을 사용하는 곳에서 발생할 수 있다.
  • 웹 사이트 화면에 출력 되는 데이터가 DB 에 보관 혹은 고정 데이터 인지 확인.
  • 어떻게 쿼리가 나오는지, like 절이 쓰여 졌는지 머리속으로 그려져야 한다.

  • mario 검색 시

  • “o” 검색 시

검색 데이터에 따라서 데이터가 오는지 확인한다.

  • search.php?search=o parameter : search : o
    1. 조건에 맞는 데이터가 나오므로 db 연동,
    2. SELECT 문을 사용할 것이고
    3. WHERE 구문 사용할 것이고
    4. LIKE 절을 사용할 것이다.
    • 이 위치에서 테스트를 해볼 수 있다.
    • query 를 사용해서 data 를 가지고 오고 있다는 것으로 판단했다.
    • 찾은 위치에서 SQL Injection 이 되는 지 테스트를 해보자.
    • where 문이 어떻게 만들어 지는지 생각해보자.
WHERER user_id like '%____%' and '1'

# 밑줄 친 곳에 테스트를 하려면 아래와 같이 맞출 것이다.
nor%' AND '1%'='1

이런 식의 query 로 만들어 질 예정이다.

  1. nor
  2. nor%’ AND ‘1%’=’1

이 두 개로 질의 했을 때 결과가 같다면, SQL 질의문을 주입 됬다고 판단 할 수 있다.

→ 여기 까지가 SQL Injection Point 를 찾는 방법.

  • 하나씩 살펴보면

    • '% 가 있으므로 nor%' 로 하나 마무리 하고
    • '1'='1' 로 True 를 만들어야 하는데 '%' 하나 있으므로
      • AND '1%' = '1 로써 마무리 시킨다.
  • payload

    nor%' AND '1%'='1

따라서 full query 는

WHERE user_id like '%nor%' AND '1%' = '1%'

로 Query 가 짜여지게 된다. 이 결과가 nor 검색으로만 했을 때의 결과와 같다면, SQL Query 를 삽입했다 라고 판단을 할 수 있다.

mari 로 해보자.

GET /sqli_1/search.php?search=mari

<td>
    <div class="widget-26-job-title">
        <a href="#">mario</a>
    </div>
</td>
<td>
  <div class="widget-26-job-title">
      <a href="#">17</a>
  </div>
</td>
<td>
  <div class="widget-26-job-title">
      <a href="#">700</a>
  </div>
</td>
<td>
  <div class="widget-26-job-title">
      <a href="#">23%</a>
  </div>
</td>
WHERE user_id like '%mari%' AND '1%' = '1%'
  • payload
    mari%' AND '1%'='1
  • request
    GET /sqli_1/search.php?search=mari%'+AND+'1%'='1 HTTP/1.1
  • response
    <td>
        <div class="widget-26-job-title">
            <a href="#">mario</a>
        </div>
    </td>
    <td>
      <div class="widget-26-job-title">
          <a href="#">17</a>
      </div>
    </td>
    <td>
      <div class="widget-26-job-title">
          <a href="#">700</a>
      </div>
    </td>
    <td>
      <div class="widget-26-job-title">
          <a href="#">23%</a>
      </div>
    </td>

조건 성립.

  • 데이터가 똑같이 나오므로, SQL Injection 이 일어나겠다. 공격 진행.

SQL Injection point 찾기 1.

  • DB 에서 데이터를 가져오는 곳.
  • 사용자의 입력 값이 글씨를 쓰는 칸 만이 있는 것이 아님.
  • DB 에서 어떤 데이터가 오고 있는 것 같다.
  • 이 DB 에 있는 데이터가 어떤 값을 통해서 오고 있다.
    • DB 에 있는 데이터가 필터링 되어서 조건문에 걸려서 데이터가 오는지.
    • BurpSuit 의 파라미터, 해더 정보 등을 분석해봐야 한다.

Blind Injection

어디서나 일어난다. 참인 조건과 거짓 조건에 따라서 결과가 다르다.

  • True 조건

  • False 조건

참과 거짓이 명확하게 구분되어 조회 된다.

주소 와 어디 파라미터, 데이터에서 SQL Injection 이 일어나는지 찾아보자.

증거는 참 / 거짓 조건이 삽입 가능 하다라는 증거만 보여주면 된다.

SQL Injection 취약점에서 찾기 1.

주소와 어느 parameter, data 에서 SQL Injection 이 일어나는지 확인해보자.

  1. SQL Injection 의 point 를 찾기 위해서는 DB 에서 데이터가 날아오는 곳에서 테스트.

  2. WEB server 에서 어떤 sql 문을 사용하고 있는 가를 그려보자.

    1. concat(0x3a, database()):database()
    2. char_length(database()) = 3 과 같은 database 길이

    의 쿼리는 로그인, 회원 가입에 필요가 없다.

  • 회원 가입이라면 INSERT, INTO, VALUES 를 사용.
  • 게시판에서는 이 파라미터들이 어디에 먼저 들어갈 것인지 그린다.
  • 개인 정보가 조회 된다면 DB 에 들어가 있고, SELECT 문이 사용되고 있다.
    SELECT ______ WHERE user='____'
    이렇게 들어와서 user 에 데이터가 들어가서 데이터를 가져 왔고 응답 데이터가 존재.
    • SELECT 문이 사용 될 것이고,

    • sessionid 에서 서버에 저장된 데이터를 가져와서 넣게 된다.

    • burp-suite 에서 확인, 쿠키 정보가 존자.

      Cookie: user=asdqwe1; PHPSESSID=4mikb1nvq37jsolsstpi3jd8fs

      user 를 사용해야 하는데, 어떻게 사용 되었을까? 를 생각해보자.

    • user='_____' 이므로, sfUser' and '1'='1 과 같은 응답 비교.

  1. 데이터 체크 확인

    asdqwe1' and '1'='1 일 때

    asdqwe1' and '1'='2 일 때

    • 개인 정보가 나오지 않았을 때, 참 과 거짓 조건에 따라서 데이터가 다르게 나온다.
    • 이 위치에서 SQL Injection 이 확인 가능하다. 여기서 데이터를 다 뽑을 수 있다.
    1. 보이지 않은 parameter 도 챙겨봐야 하고
    2. parameter 뿐만 아니라 쿠키에 있는 데이터를 조회할 수 있고,
    3. User Agent 를이용해서 SQL Injection 이 가능할 수도 있다.
    4. 보통은 Cookie 이다. Cookie 를 이용해서도 SQL Injection 이 가능하다.
    5. 입력하는 글자 칸만 SQL Injection 이 가능한 것은 아니다. Server 로 데이터를 보내면 만약에 그 데이터를 이용하여 SQL 질의를 한다면 SQL Injection 을 확인해 봐야 한다.
    6. 들어가는 데이터가 서버에서 완성되서 이 결과가 나오고 있구나를 그리고 확인한다.

SQL Injection point 찾기 2.

  • 게시판

검색 기능 사용시 Title 이 test 가 있다. DB 에 게시판 정보들이 있는데, 화면에 list 를 뿌려주고 있다.

  • option_val : title (작성자, 제목 … 등등 의 옵션 파라미터)
  • board_result : test (글 제목)
  • board_search : %F0%9F%94%8D 돋보기 아이콘.

이런 parameter 들이 서버 측에 들어가서 어떻게 동작하는지 확인.

  1. board_result 확인.

    SELECT ~~~ WHERE LIKE '%____%'로 구성될 것이고,

    testtest%' and '1%'='1 과 같이 비교를 해보고 결과가 같은지 다른지 보자.

    → 존재하지 않는다로 나와서, candidate 에서 제거.

  1. option_value 를 확인.

    SELECT ~~~ WHERE ______ LIKE '%asdqwe1%'

    title 과 함께 조건을 넣기 위해서 SQL 문이 __ 에 알맞게 작성되어 들어가야 한다.

    1. SELECT ~~~ WHERE title LIKE '%asdqwe1%'
    2. SELECT ~~~ WHERE '1'='1' and title LIKE '%asdqwe1%'

    뒷 부분을 맞춰서 '1'='1' 을 앞에 넣고 condition 넣을 공간을 만든다.

모의 해킹 서버에서 # 을 써버리면, 필요한 정보의 Query 를 날리게 된다. 실제 대형 사이트는 10줄, 20줄 짜리 SELECT 가 된다. 따라서 주석을 쓰는 것은 위험하다.

  • '1'='1' 의 참인 결과가 나왔다.
option_val='1'='1'+and+title&board_result=test&board_search=%F0%9F%94%8D&date_from=&date_to=

  • '1'='2' 인 거짓 결과가 나왔다.
option_val='1'='2'+and+title&board_result=test&board_search=%F0%9F%94%8D&date_from=&date_to=

⇒ SQL Injection Point 를 찾을 수 있다.

  • 433'%09and%09'1'='1 일 때

433'%09and%09'1'='2 일 때

SQL Injection 가능한 방법들

  1. Cookie 데이터로 SQL Injection 이 가능.
  2. Column 이름에 SQL Injection 이 가능.
    1. option_val=username&board_result=asdqwe1&board_search=%F0%9F%94%8D&date_from=&date_to=&sort=title
    2. option_val=username
    3. board_result=asdqwe1
    4. sort=title

WHERE username LIKE ‘%asdqwe1% ORDER BY titile

  • 참/거짓 조건을 어떻게 ORDER BY 안에 넣을까?

  • case when

    CASE WHEN (조건) THEN (참일때) ELSE (거짓일 때) END

    CASE WHEN (1=2) THEN 1 ELSE 2 END

    option_val=username&board_result=asdqwe1&board_search=%F0%9F%94%8D&date_from=&date_to=&sort=case when (1=1) then username else title end

    이것만 가지고 SQL Injection 이 가능하다.

    • 참이면 username 이 sort 될 것이고,
    • 거짓이면 title 이 sort 될 것이다.
  • 들어가고 있는 데이터가 order by 절에 들어가고 있다고 판단.

  • case when 이라는 문법을 사용할 수 있다.

참과 거짓을 분별할 수 있는 조건을 삽입할 수 있다. 참과 거짓 조건이 잘 활용되면, 데이터를 한 글자 씩 뽑아낼 수 있다.

  • parameter
option_val=username&board_result=d4r6j&board_search=%F0%9F%94%8D&date_from=&date_to=&sort=title

===
option_val=username
board_result=test
sort=title

# 내가 들어간 데이터가 ORDER BY 절에 들어간다고 추론한다고 생각하고 있다.
===
WHERE username like '%test%'
ORDER BY title

title 대신 1, 2

title 대신 999999

column index 를 넘어가게 된다.

참과 거짓 조건을 order by 에 어떻게 넣을까? case when

case when (조건) then (참일때) else (거짓일 때) end
case when (1=2) then 1 else 2 end
option_val=username&board_result=d4r6j&board_search=%F0%9F%94%8D&date_from=&date_to=&sort=case when (1=1) then username else title end
  • 조건이 참 이 때는 username 이 들어올 것
  • 조건이 거짓일 때는 title 이 들어올 것

username 이 사용 되었을 때와 title 이 사용되었을 때의 결과가 다르다는 것을 확인 하였고 위와 같이 조건을 넣고 참일 때, 거짓일 때 조건을 넣는다. 즉, 참과 거짓을 분별할 수 있는 조건을 삽입할 수 있다고 판단할 수 있다.

→ 이것은 참과 거짓 조건이 잘 활용 되면 데이터를 한 글자 씩 뽑아낼 수 있다.

Column 명을 잘 모를 때 사용.

# column 명을 잘 모를 때가 있다. 그 때는 거짓일 때의 결과를 정확히 찝어줄 수 가 없다.
case when (1=1) then 1 else (error 를 유발하는 데이터) end
# string 이면 999999 도 가능하므로 문자열 정렬이 된다.
case when (1=1) then 1 else (9999999) end 
case when (1=1) then 1 else (select 1 union select 2) end

case when select 1 union select 2 는

# 행이 1개가 나와야 하는데 2개가 나왔다.
(select 1 union select 2 where (1=1)) 
# 참이어서, matrix 가 나와서 오류.
# 거짓이어서, matrix 가 아닌 select 1 만 나와서 정상.

select 1, 2 가 같이 나오는 matrix 처럼 나오느냐의 조건은 where (1=1) 이 된다. 아니면 select 1 만 나오겠지.

Error 를 유발해야 될 때.

  • 일부로 error 로 유발을 해서 SQL Injection Point 를 찾는 곳이 있다.

    마이 페이지에서 위와 같은 정보고 오고 있다는 것을 알았다.


Cookie: user=qweasd1; PHPSESSID=8g89pqpgvpjj9g6m21qi694h0u

쿠키에 데이터가 존재한다고 해서,

Cookie: user=qweasd1' and '1'='1; PHPSESSID=8g89pqpgvpjj9g6m21qi694h0u

'1'='1 (참 조건) 데이터가 나오게 된다.

'1'='2 (거짓 조건) 도 데이터가 나오게 되면서 Sql Injection point 가 아니게 된다.

작은 따옴표만 주었을 때는 DB Error 가 난다. 이것을 활용할 수 있다.

  • SQL Injection 이 일어나는데 참/거짓 조건의 결과가 똑같이 나올 때가 있다. 그런데 DB E rror 가 떳다면 의심 범위가 된다.
  • 이와 같을 때 SQL Injection 이 가능하다 라고 확인하기 위해서는 Error 를 유발하는 코드를 넣어야 한다.
  • error 를 유발하는 코드를 삽입해야 하는데, 조건에 따라서, 참 인 조건에서는 에러가 나지 않고, 거짓 조건에서는 에러나 난다 던가 해야 한다. 조건에 따라서 에러가 바뀌어야 한다.

error 를 유발하는 코드를 삽입할 때 자주 쓰는 코드는 SELECT 1 UNION SELECT 2 WHERE (1=1) [ (1=2)] : 개인적으로 자주 사용하는 구문.

Blind SQL Injection 을 실전에 나가서 하면 ERROR 를 SQL Injection 에 어떤 쿼리를 넣어서 error 유발을 해야 하는데 (SELECT 1 UNION SELECT 2) 와 같은 구문을 사용한다.

  • 참, 거짓 결과가 구분이 안되거나
  • 에러를 유발해야 할 때

(1=1) 참 일때 에러가 난다.

Cookie: user=qweasd1' and (SELECT 1 UNION SELECT 2 WHERE (1=1)) AND '1'='1; PHPSESSID=8g89pqpgvpjj9g6m21qi694h0u

(1=2) 거짓 일때는 에러가 나지 않는다.

실전에서 SQL Injection 이 될 것 같은데, Error 를 유발할 때 사용하는 구문

(SELECT 1 UNION SELECT 2)

참, 거짓 결과가 구분이 안되거나, error 를 유발해야 할 때 이런 구문을 사용한다.

Conclusion

SQL Injection Point 에서 가장 많이 쓰는 것.

  1. cookie, HTTP 요청 헤더 도 가능하다.
  2. column 에서도 가능하다.
  3. order by 절 SQL Injection 이 가능하다.

실제로 order by 절 에서 SQL Injection 이 가장 많이 일어난다.

SQL Injection 대응 방법

Prepared Statement

→ 일반적으로 SQL Query 문 자체가 변했었는데

SELECT ~~~ WHERE ID = '______'

→ SQL Query 를 미리 컴파일 한다는 내용.

Prepared Statement 를 사용하면 아래와 같이 구조가 변할 수가 없다.

1010100101____01010100100110
  • 기존 : 입력 데이터가 들어오고, 완성 Query 가 DB 에 전달 되고, DB 에서 컴파일.
  • 지금 : 미리 컴파일 하여 기계어로 바꾼다. 필요한 데이터만 ? 로 구멍을 뚫는다.
  • 미리 컴파일을 하면, 속도가 빠르고 보안도 좋아지게 된 케이스.

따라서 미리 컴파일 해두면, “Prepared Statement” 를 사용하면, 구조가 변할 수가 없다.

질의문이 바뀌지 않으므로 문자 그대로 받아들여진다.

  1. Prepared Statement 를 잘못 사용하는 경우, 취약점 가능.
  2. Prepared Statement 를 적용을 못 하는 경우.
    1. order by 절 에는 ? 를 사용하지 못한다.
    2. table, column name
      where ___ like : column 이름에서 prepared statement 에서 사용하지 못한다.
  • 그래서 parameter 에 order by, sort 는 injection 확인 검사가 필요하다.
  • 실제로 order by 절 에서 SQL Injection 이 가장 많이 일어난다.
  1. White List Filtering

    단어를 기반으로 어떤 글자를 제한 하는 것.

    • 화이트 리스트, 블랙 리스트 들이 있다.

    어떤 글자를 제한 하는 것.

    blacklist : 특정 단어를 못 쓰게 하는 것.

    whitelist : 특정 단어만 쓸 수 있게 하는 것.

    if (sort == ''title') {
    
    } elif (sort == 'author')
    
    } else  {
        sort = 'title';
    }

    이런 식으로 white list를 사용.

0개의 댓글