자습시간

[MySQL] Information_schema

happy-nut 2015. 12. 29. 00:55


MySQL은 가장 많이 쓰이는 데이터베이스(Database) 중 하나입니다. 

(현재 이 글을 쓰는 시점 세계에서 2번째로 많이 쓰인다고 합니다)

그럼 데이터베이스란 무엇일까요? 데이터베이스는 위키백과에서 다음과 같이 정의하고 있습니다.


" 여러 사람들이 공유하고 사용할 목적으로 통합 관리되는 정보의 집합이다. 논리적으로 연관된 하나 이상의 자료의 모음으로 그 내용을 고도로 구조화함으로써 검색과 갱신의 효율화를 꾀한 것이다. 즉, 몇 개의 자료 파일을 조직적으로 통합하여 자료 항목의 중복을 없애고 자료를 구조화하여 기억시켜 놓은 자료의 집합체라고 할 수 있다. "


쉽게 말하자면 "데이터의 집합"이라는 뜻입니다. 

하지만 데이터의 종류가 너무 많아질 경우 이 또한 목록화 시킬 필요가 생기는데, 이러한 요구에 맞춰 생겨난 게 바로 메타데이터(metadata) 입니다. 메타데이터는 데이터베이스와 같이 빅데이터를 다뤄야 하는 경우에 효율적인 정보처리를 위해 만든 것으로, 쉽게 말하자면 "정보에 대한 정보" 입니다.

따라서 MySQL도 예외없이 메타데이터를 가지고 있습니다. MySQL은 메타데이터들을 종류별로 묶어 테이블을 만들었고, 이 테이블을 모아 데이터베이스를 만들었는데, 그 데이터베이스가 바로 INFORMATION_SCHEMA입니다. MySQL을 설치해 보신 분들은 자신이 만들지도 않은 데이터베이스가 MySQL이 설치되자마자 만들어져 있던 것을 한번 쯤 확인해보셨을 겁니다.

INFORMATION_SCHEMA는 MySQL서버가 운영하는 모든 다른 데이터베이스에 대한 정보를 저장하는 장소입니다. 그 중에는 데이터베이스 이름, 테이블 이름, 컬럼의 자료형, 접근 권한 처럼 SQL Injection공격에 이용될 정도로 민감한 정보도 포함되어 있습니다.


그렇다면 이제, 따분한 상식은 집어치우고 해커의 입장이 되어볼 차례입니다.

만약 내가 SQL Injection 취약점이 있는 웹 어플리케이션을 발견했는데도 불구하고, 컬럼 이름이나 테이블 이름을 알아내지 못해 원하는 공격을 하지 못한 체로 몹시 끙끙 앓고 있다고 해봅시다.(실제로는 완전하게 불법이 되기 직전인 상황입니다. 절대 인가받지 않은 사이트에 공격을 해선 안됩니다) 이런 상황에 해커는 저 황금같은 메타데이터를 사용하지 않을 수 없습니다. 

개발자가 실수로 INFORMATION_SCHEMA에 대해 신경을 못 써줬기만 한다면(보통 자신이 만들 어플리케이션과 전혀 관련없는 INFORMATION_SCHEMA에 대해 신경쓰는 개발자는 잘 없습니다) 이 INFORMATION_SCHEMA에 대해 아느냐 모르느냐가 SQL Injection 공격 성공 여부를 결정짓게 됩니다.

보통 공격자들이 처음 관심을 가지는 것은 '내가 공격할 어플리케이션이 어떤 권한으로 쿼리를 넘기는지' 입니다.

이는 다음 쿼리문으로 확인해 볼 수 있습니다.(이후에 소개되는 모든 쿼리문들은 MySQL 5.7에서 테스트되었음을 밝힙니다.)

SELECT user()

SELECT system_user()

= 이와 비슷하게 SELECT current_user;가 있는데, 이는 현재 MySQL서버에 접속한 유저목록을 출력합니다.


이제 어떤 사용자로 쿼리를 넘기는지 알았으니, 해당 MySQL서버에 어떤 데이터베이스들이 있는지 알고 싶다면 다음 쿼리문을 사용해 볼 수 있습니다.

SELECT schema_name FROM information_schema.schemata;

= 해당 쿼리는 SHOW databases; 와 동일한 효과를 냅니다.

보통 웹 어플리케이션에서 데이터를 처리할 때 SELECT문은 써도 SHOW문은 쓰질 않으니, 그 동안에는 전체 데이터베이스 목록만큼은 쉽게 알 수 없다고 생각하고 있었을 지 모릅니다. 그러나 만약 웹 어플리케이션에 SELECT문을 쓰는 지점에서 취약점이 있다면 위와 같은 방법으로 희생자 서버에서 어떠한 데이터베이스들이 있는지 조회할 수 있게 됩니다. 

이제 어떤 데이터베이스들이 있는지 알았으니 어떤 테이블들이 있는지 궁금할 것입니다. 

이는 다음 쿼리문으로 확인할 수 있습니다.

SELECT table_schema, table_name FROM information_schema.tables where table_schema = '데이터베이스 명';

해당 쿼리는 USE 데이터 베이스 명; SHOW tables; 와 동일한 효과를 냅니다.

마찬가지로, SHOW가 아닌 SELECT로 테이블 목록을 가져오는 것에 주목합니다.

만약 현재 어플리케이션이 사용하는(선택한) 데이터베이스를 보려면 다음쿼리를 이용할 수 있습니다.

SELECT database();


이제 어떤 테이블들이 있는지 알았으니 어떤 컬럼들이 있는지 궁금할 것입니다.

이는 다음 쿼리문으로 확인할 수 있습니다.

SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema' AND table_schema != 'performance_schema' AND table_schema != 'sys';


만약 이게 너무 길다면 다음과 같이 직접 DB명을 명시하여 데이터를 추출할 수 있습니다.

SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = '데이터베이스 명';


만약 특정 테이블에 있는 컬럼들만 궁금하다면 다음과 같이 사용할 수 있습니다.

SELECT column_name FROM information_schema.columns WHERE table_name = '테이블 명';


컬럼명까지 모두 알아냈으므로 안에 있는 Data만 탈취한다면 해커의 목적은 달성됩니다.


MySQL은 비단 데이터베이스의 구조뿐만이 아니라 데이터베이스 사용자의 권한에 대한 모든 정보도 끌어올 수 있습니다.

어떤 사용자가 어떤 권한을 가지는지 알고 싶다면 다음 쿼리를 사용할 수 있습니다.

SELECT grantee, privilege_type, is_grantable FROM information_schema.user_privileges;


MySQL버전 또한 메타데이터에 포함되어 있습니다.

SELECT @@version;


이처럼 INFORMATION_SCHEMA의 활용가능성은 무궁무진합니다. 

대부분의 INFORMATION_SCHEMA안의 Table들은 읽기 권한만 가지고 있어서 인위적인 수정이 불가능하지만, 안심할 수 없음에는 변함이 없습니다. 치명적인 정보유출을 막기위해, 개발자들은 이 데이터들이 해커들에게 노출되지 않게 잘 신경을 써야겠습니다.