ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [웹해킹 #2] SQL Injection
    EVI$ION/WEB HACKING 2019. 4. 9. 00:00

    [SQL Injection - WHERE 구문 우회, UNION 공격]

    SQL 인젝션이란, 데이터베이스에 전송되는 SQL 쿼리문을 조작하여 데이터를 변조하거나, 허가되지 않은 정보에 접근하는 공격이다. 주로 개인정보를 빼낼 때 많이 사용되는 기법이다.

     

    정상적인 웹 요청 및 응답에서는, 사용자가 ID를 입력하면 서버는 SQL 쿼리문을 통해 데이터베이스에 접근하여 해당 ID가 있는지 검색하고, 정보가 있으면 검색 결과를 사용자에게 전송한다. 이때의 SQL 쿼리문은 아래와 같다.

    $id = $_REQUEST['id'];
    $query = "SELECT name, email FROM users WHERE id = '$id';";

     

    그러나 만약 ID 입력칸에 1' or '1'='1을 입력한다면, WHERE문의 조건이 id = '1' 또는 '1'='1'이 되어 항상 True인 구문이 되어버린다. 즉, WHERE 구문이 우회된 채로 SQL 쿼리문이 실행되기 때문에 데이터베이스 내의 모든 사용자의 정보가 노출된다.

    SELECT name, email FROM users WHERE ID='1' or '1'='1';

     

    만약 공격자가 ID 입력칸에 1' UNION SELECT name, pw FROM users#을 입력한다면, 공격자가 입력한 SELECT문이 그대로 실행되어 users 테이블의 pw와 name이 모두 출력된다. UNION은 2개 이상의 쿼리를 한번에 요청하여 그 결과를 하나의 테이블에 합쳐서 출력해주는 연산자로, 공격자는 이를 악용하여 원래의 요청에 하나의 쿼리를 추가로 삽입해 원하는 정보를 얻을 수 있다. #은 MySQL에서 주석 처리에 사용되는 문자로, 원래의 쿼리문에서 맨 마지막에 남은 작은 따옴표(')로 인한 오류를 방지하기 위해 사용된다.

    SELECT name, email FROM users WHERE id = '1' UNION SELECT name, pw FROM users#'

    그러나 UNION을 이용한 SQL 인젝션을 시도하기 위해서는 두 쿼리의 칼럼 개수와 데이터 형식이 같아야 하고 칼럼명, 테이블명 등을 알아야 하기 때문에, 먼저 공격 대상의 데이터베이스 정보를 충분히 수집해야 한다.

     

    SQL Injection (WHERE 구문 우회, UNION 공격) 실습

    DVWA를 실행하여 SQL Injection 메뉴에 들어간다.

    DVWA - SQL Injection

    SQL 인젝션 실습 전, 공격 대상 웹이 SQL 인젝션에 취약한 환경인지 알아보기 위해 작은 따옴표(')를 입력해본다. 

    작은 따옴표(') 입력

    아래와 같은 오류 문구가 출력되면 SQL 인젝션에 취약한 웹 페이지이다.

    오류 메시지

    위와 같은 오류가 출력된 이유는 $query 부분의 $id에 작은 따옴표(')가 들어갔기 때문이다.

    DVWA - SQL Injection - View Source

    1. WHERE 구문 우회

    WHERE 구문 우회

    WHERE 구문을 우회하는 코드를 작성해 제출한 결과, 해당 데이터베이스 내 전체 사용자의 정보가 출력된다.

    WHERE 구문 우회 결과

     

    2. UNION을 이용한 SQL 인젝션

    앞서 말한대로 UNION을 이용한 공격을 하기 위해서는 원래 쿼리문이 조회하는 SELECT문의 칼럼 개수와 UNION 뒤의 SELECT문에서 요청하는 칼럼 개수가 같아야 한다. 따라서 원래 쿼리문에서 조회하는 칼럼의 개수를 먼저 알아야 하는데, 이는 ORDER BY 구문을 통해 알아낼 수 있다.

     

    ORDER BY 구문은 지정된 칼럼을 기준으로 결과를 정렬해주는 키워드로, 전체 칼럼의 개수보다 더 큰 값을 입력하면 오류가 발생한다.

     

    전체 칼럼의 개수와 같을 때
    전체 칼럼의 개수보다 클 때

    위의 캡처 화면처럼, 1' ORDER BY 1#, 1' ORDER BY 2#을 입력했을 때에는 정상적으로 데이터가 조회되지만, 1' ORDER BY 3#을 입력했을 때에는 오류 문구가 출력된다. 따라서 원래 SELECT문에서 지정한 칼럼의 수는 2개임을 알 수 있다.

     

    칼럼의 개수가 2개인 것을 알았으니 UNION을 사용해 SQL 인젝션을 시도하면 내가 입력한 데이터가 First name, Surname 칼럼에 삽입된 것을 확인할 수 있다.

    숫자형 데이터 삽입
    문자형 데이터 삽입

     

    MySQL에는 Information_schema라는 데이터베이스가 존재하는데, 이곳에선 MySQL 서버가 운영하는 모든 데이터베이스에 대한 정보(데이터베이스 이름, 테이블, 칼럼 정보 등)를 관리한다. 따라서 SQL 인젝션을 시도할 때, 공격 대상에 대한 정보를 수집하기 위해서 반드시 조회해야 하는 DB이다. Information_schema에는 여러 테이블이 존재하는데, 주로 SQL 인젝션에서 유용하게 사용될 정보들이 저장된 테이블은 schemata, tables, columns이다.

     

    UNION SQL 인젝션을 이용하여 먼저 information_schema DB의 schemata 테이블에 접근한다. (이때 schema_name과 함께 SELECT문에 삽입한 2는 UNION 연산자 때문에 칼럼 개수를 맞춰주려고 넣은 인자이다.) 

    ' UNION SELECT schema_name, 2 from information_schema.schemata#

    Information_schema - schemata 테이블 접근

    schemata 테이블 접근을 통해 해당 웹페이지에서 사용하는 데이터베이스 이름이 dvwa임을 알았으므로, 알아낸 DB명을 이용하여 해당 DB 내의 테이블명을 찾는다.

    ' UNION SELECT table_name, 2 from information_schema.tables WHERE table_schema='dvwa'#

    Information_schema - tables 테이블 접근

    tables 테이블 조회를 통해 dvwa 내 테이블 이름이 users임을 알 수 있다. 이제 users 테이블 내에 있는 칼럼의 이름을 찾아보자.

    ' UNION SELECT column_name, 2 from information_schema.columns WHERE table_schema='dvwa' and table_name='users'#

    Information_schema - columns 테이블 접근

    여러 개의 칼럼명이 나오는데, 그 중 필요한 것은 user와 password 칼럼이므로 UNION SQL 인젝션을 통해 해당 칼럼들의 정보를 조회하면 모든 사용자의 아이디와 비밀번호를 확인할 수 있다.

    ' UNION SELECT user, password FROM users#

    users 테이블의 user, password 칼럼 정보

     

    [Blind SQL Injection]

    블라인드 SQL 인젝션은 에러 기반의 SQL 인젝션에서처럼 직접적인 결과를 알 수 없고 참/거짓 여부만을 알 수 있을 때 사용되는 공격 기법으로, 참일 때와 거짓일 때의 결과 차이를 분석하여 정보를 얻어낸다.

     

    DVWA에서 Blind SQL Injection 메뉴에 들어가 아무 아이디나 입력해보면, DB 내에 해당 아이디가 존재하는지 아님 없는지 존재 여부만을 알려준다.

    ID가 DB에 없는 경우

    블라인드 SQL 인젝션에서는 아래 예시처럼 결과값을 통해서 SQL문이 참인지 거짓인지만 판별할 수 있기 때문에, AND 연산자를 이용해 입력값을 보내고, 그 결과값의 변화 여부에 주목하여 정보를 알아내야 한다. 일단 참/거짓에 따라 결과가 다르면 해당 웹페이지는 SQL 인젝션에 취약하다는 것을 알 수 있다.

     

    그러나 만약 참/거짓에 관계 없이 출력되는 메시지가 같아 참/거짓 여부를 알 수 없다면, 웹 요청에 대한 응답 시간의 차이를 이용해야 한다. 주로 sleep이나 waitfor 함수를 사용한다.

    sleep 함수는 MySQL에서 쿼리 수행을 의도적으로 지연시킬 때 사용되는 함수로, sleep(5)를 실행하면 쿼리 수행이 5초동안 멈춘다.

     

    만약 입력값이 참이면 sleep 함수가 정상적으로 실행되어 응답 시간이 5초 이상 걸리게 되고, 거짓이면 sleep 함수가 실행되지 않아 응답 시간이 짧다.

    참일 때 → sleep 실행

    1' and 1=2 and sleep(5)#

    거짓일 때 → sleep 실행 X

     

    [SQL Injection 공격 대응 방법]

    DVWA에서 Security Level이 impossible일 때의 소스 코드

    1. 입력값 검증

    필요한 입력 데이터의 형식을 먼저 검사하여 일차적으로 불필요한 데이터를 걸러내는 것이다.

    예를 들어 필요한 입력값이 숫자형이면 쿼리를 수행하기 전에 is_numeric 함수를 사용해 입력값이 숫자인지 아닌지를 판단하여 숫자가 아닐 경우 쿼리를 수행하지 않는다.

    또한 SQL 인젝션에서 자주 사용되는 특수 문자나 문구들(', #, =, UNION, AND, SELECT, FROM 등)을 필터링하는 함수를 사용함으로써 SQL 인젝션을 방어할 수 있다.

     

    2. 파라미터 쿼리 이용

    prepared statement를 사용하면, 컴퓨터가 prepare의 인자로 들어간 SQL문 전체를 문자열로 인식하기 때문에 공격자가 파라미터를 조작하기 어렵다.

     

     

    참고 문헌

    http://coashanee5.blogspot.com/2017/02/sql.html
    https://mrrootable.tistory.com/25
    http://www.mysqlkorea.com/sub.html?mcode=manual&scode=01&lang=k&cat1=20
    https://poqw.tistory.com/24

     

Designed by Tistory.