[Web Hacking] SQL Injection : 여러 Case 및 대응 방안 #5

2023. 12. 16. 01:50정보 보안/Web

 

SQL Injection 공격이 가능하려면?

 

  SQL Injection 공격은 주로 사용자가 입력을 할 수 있는 입력창에서 발생한다. 그러나 입력창이 아니더라도 SQL Injection 공격은 DB와 관련된 곳이라면 어디든지 발생할 수 있다는 것을 인지해야 한다.

 

  SQL Injection 공격이란 공격자의 입맛에 맞게 SQL 질의문을 삽입함으로써 공격대상의 DB에 존재하는 데이터를 추출하는 것으로 목표로 한다. 그렇다는 것은 SQL Injection 공격은 DB와 연결되어 있으며 SQL 질의문이 사용되는 위치라면 어디든지 가능하다는 의미이다.

 

*SQL Injection 공격이 발생 가능한 조건

 

  1. DB와 연결되어있어야 한다.
  2. SQL 질의문을 사용하여야 한다.
  3. SQL Injection 공격 시 참과 거짓에 결과를 구분할 수 있어야 한다.

 

SQL Injection 공격이 가능한 여러 Case들

 

  SQL Injection 공격은 사용자 입력창뿐만 아니라 다른 위치에서도 가능하다. 다른 위치에서의 SQL Injection 공격을 수행하기 위해 웹 프록시 툴 중 하나인 Burp Suite을 사용하기로 한다.

 

[1] Cookie에서의 SQL Injection

 

 

  첫 번째 Case는 Cookie에서의 SQL Injection이다. 웹 페이지 중에서 사용자의 개인정보를 확인하는 '마이페이지'라는 곳이 존재한다. 웹 개발을 하다 보면 마이페이지는 사용자의 개인 정보 조회 시 사용자 측에서 웹 페이지 요청 시 함께 전달하는 SESSION ID로 사용자를 식별한 후 해당 사용자의 개인 정보를 DB에서 조회하여 웹 페이지에 출력한다는 것을 알 수 있다. (지금 사용 중인 웹 페이지는 해당 Case가 존재한다라는 것을 알려주기 위해 의도적으로 user라는 쿠키명에 담겨있는 쿠키값인 사용자 계정을 참고하여 개인정보를 조회하도록 구성되어 있다.)

 

 

  user라는 쿠키의 쿠키값에 SQL Injection 공격을 시도한 결과 성공적으로 수행된 것을 확인할 수 있다. 그렇다면 DB에 존재하지 않는 계정이 삽입되면(거짓 결과를 유발하는 값) 어떤 정보를 응답하는지 알아보자.

 

 

  user 쿠키의 쿠키값으로 and '1'='2라는 질의문을 삽입한 결과 정보가 출력되는 세 부분 중 가운데 부분의 'Nothing Here...' 문자열이 출력되지 않았다. 이를 통해 SQL Injection 공격으로 참과 거짓에 대한 결과를 분별 가능함으로써 해당 부분에서 Blind SQL Injection 공격을 수행할 수 있다.

 

[2] 질의문에 삽입되는 위치가 컬럼명 혹은 테이블명인 경우

 

 

  두 번째 Case는 질의문에 삽입되는 위치가 컬럼명 혹은 테이블명인 경우이다. 이번 Case는 게시판 페이지에서 게시글 조회 시 입력 문자열을 포함하는 곳을 전달하는 파라미터에 SQL Injection 공격이 가능하다. 게시글 조회 시 사용되는 질의문은 일반적으로 'SELECT * FROM TABLE WHERE COLUMN LIKE '%조회 문자열%'으로 구성되며 COLUMN 위치에 '작성자, 제목, 내용'이라는 데이터를 조건에 맞춰 선택한다. 해당 웹 페이지에서는 option_val라는 파라미터에 COLUMN(작성자, 제목, 내용)에 해당하는 데이터를 담아 전달하며 서버 측에서는 전달받은 데이터를 기준으로 게시글을 출력한다.

 

 

  컬럼명 혹은 테이블명에서의 SQL Injection 공격을 수행하기 위해서는 생각을 달리 해야한다. 지금까지는 SQL 질의문이 삽입되는 위치가 컬럼명 혹은 테이블명 다음에 위치하기 때문에 '입력 문자열+삽입 문자열' 형태로 구성하여 SQL Injection을 수행했지만 이번에는 '삽입 문자열+입력 문자열'처럼 구성해야 SQL 문법이 망가지지 않는다. 그렇다면 DB에 존재하지 않는 컬럼명이 삽입되면 어떤 정보를 응답하는지 알아보자.

 

 

  option_val 파라미터에 '1'='2' and username 질의문을 삽입한 결과 어떤 게시글도 출력되지 않았다. 이를 통해 SQL Injection 공격으로 참과 거짓에 대한 결과를 구분 가능함으로써 해당 부분에서 Blind SQL Injection 공격을 수행할 수 있다.

 

[3] 질의문에 삽입되는 위치가 ORDER BY 구문인 경우

 

sort = title인 경우
sort = content인 경우

 

  세 번째 Case는 질의문에 삽입되는 위치가 ORDER BY 구문인 경우이다. ORDER BY 구문은 데이터를 정렬할 때 사용하는 구문으로서 게시판 페이지에서 제목, 내용, 그리고 작성일 등을 기준으로 게시글을 정렬하여 나열하는 용도로 쓰인다. 위 사진에서 웹 페이지를 요청할 때 sort 파라미터가 같이 전달되며 sort = 'title'이면 게시글의 제목을 기준으로 게시글을 정렬하여 나열하고 sort = 'content'이면 게시글의 내용을 기준으로 게시글을 정렬하여 나열한다. ORDER BY 구문은 ORDER BY  '컬럼명 혹은 숫자 1,2,3...'으로 이루어져 있으며 쓰인 컬럼명을 기준으로 정렬되며 기본값은 오름차순 정렬이다.(숫자는 select 구문에 쓰인 컬럼들의 순서를 나타낸다.)

 

  그렇다면 ORDER BY 구문에서는 어떻게 SQL Injection이 이루어지는지 알아보자. 

 

 

  ORDER BY 구문에서는 주로 CASE WHEN  구문으로 SQL Injection 공격을 수행한다. 우선 CASE WHEN  구문에 대해 간단히 설명하자면 SQL에서의 IF문이라고 생각하면 된다. 'CASE WHEN (조건) THEN (값 1) ELSE (값 2) END'으로 쓰이며 조건으로 출력되는 값이 TRUE이면 값 1을 출력하고 FALSE 값 2를 출력하는 방식이다. 이러한 방식을 해당 웹 페이지에 적용하여 sort 파라미터에 'sort = CASE WHEN (1=1) THEN (title) ELSE (content) END' 이러한 질의문을 삽입할 수 있다. 조건의 결과가 참이면 title을 출력하므로 게시글은 제목을 기준으로 정렬 후 나열되며 '1=1' 대신 '1=2'를 입력하게 되면 조건의 결과가 거짓이므로 content를 출력하여 게시글을 내용 기준으로 정렬 후 나열되는 것으로 참과 거짓의 결과를 구분할 수 있게 된다.

 

조건의 결과가 참인 경우
조건의 결과가 거짓인 경우

 

  그러나 위에 쓰인 방법으로 참과 거짓을 판단하게 되면 가끔가다 실수를 할 수 있게 된다. 그래서 확실하게 참과 거짓을 판단하는 구문으로 'CASE WHEN (1=1) THEN 1 ELSE (select 1 union select 2) END'이러한 형태를 사용하기도 한다. 해당 구문은 ORDER BY 구문에 입력되는 값이 1개여야만 하는 것에 착안한 방법으로서 만약 출력되는 값이 적합하지 않다면 에러를 유발한다. 해당 원리를 이용하여 거짓의 결과로 출력되는 구문인 'select 1 union select 2'를 사용함으로써 출력 결과로 두 개의 행을 출력시킨다. 그렇게 되면 ORDER BY 구문은 두 행 중 어느 것도 선택하지 못해 에러를 유발하여 게시글을 출력하지 않는 현상을 야기한다. 이를 통해 SQL Injection 공격으로 참과 거짓에 대한 결과를 구분 가능함으로써 해당 부분에서 Blind SQL Injection 공격을 수행할 수 있다.

 

SQL Injection 공격 대안

 

  SQL Injection 공격은 가장 난이도가 쉬우면서도 공격의 효율이 좋은 공격이다. 그러나 이러한 SQL Injection 공격은 현재까지 알려진 방법 중 하나인 Prepared Statement으로 쉽게 방어할 수 있다.

 

* Prepared Statement란?

  Prepared Statement는 SQL 질의문을 미리 컴파일하여 DB에 영향을 주는 부하를 줄이기 위해서 개발되었다. 그러나 Prepared Statement를 사용하다 보니 SQL Injection 공격을 방지해 주는 효과를 발견하게 되면서 SQL Injection 공격을 방어하는 방법 중 하나로 알려지게 되었다.

  Prepared Statement는 'select * from table1 parms1 = value1 and parms2 = value2'라는 질의문에서 value1과 value2 위치를 '?'로 대체하고 나머지 부분을 미리 컴파일해 둔다. 그렇게 되면 value1과 value2로 삽입되는 문자열을 하나의 데이터로 받아들이게 된다. 이러한 효과로 인해 value1과 value2로 삽입되는 질의문이 기존의 질의문을 변조시킬 수 없게 됨으로써 SQL Injection 공격을 방어할 수 있게 된 것이다.
SQL Injection 공격 전
SQL Injection 공격 후

 

  그러나 Prepared Statement라는 방법이 존재함에도 불구하고 SQL Injection 공격이 가능한 경우가 존재한다.

 

  1. Prepared Statement를 제대로 사용하지 못한 경우 : 해당 경우에는 Prepared Statement 방법을 적용을 시키긴 했지만 어설프게 적용시킴으로써 SQL Injection 공격이 가능한 경우다.
  2. Prepared Statement를 적용시키지 못하는 경우 : 해당 경우에는 Prepared Statement를 적용시키지 못하는 구문에서 SQL Injection 공격이 발생하는 경우다. Prepared Statement를 적용시키지 못하는 위치는 ORDER BY 구문, TABLE 이름, COLUMN 이름 위치가 해당된다.

 

  1번의 경우에는 해결할 수 있는 여지가 존재하지만 2번의 경우에는 Prepared Statement 자체를 사용하지 못하기 때문에 다른 방법으로 대체해야 한다. Prepared Statement 대신 SQL Injection을 방어할 방법으로 사용되는 기법은 화이트 리스트 기반 필터링 방법이 존재한다. 화이트 리스트 기반 필터링은 대상 위치에 개발자가 입력한 단어들만 입력할 수 있는 방법으로 블랙리스트 기반 필터링 방법보다 자주 사용된다.