본문 바로가기
해킹 공부/웹해킹

SQL injection + INFORMATION_SCHEMA

by zzzmilky 2022. 1. 20.

방학동안 webhacking.kr, LOS를 중점적으로 풀고 있는데 카톡에 풀이 대충만 적어놓고 블로그엔 한꺼번에 올려야지 계속 미루다가..오랜만에 글을 쓰게 되었다.

마침 동아리에서 스터디 진행하면서 발표 자료를 만들게 됐는데 그거 바탕으로 블로그에 글 쓰게 되었다. 어려운 내용은 아니지만 SQL injection 기법들을 다양하게 익히는 중이라 한번 정리해보고 싶었다. 


INFORMATION_SCHEMA란?

Database에는 메타데이터가 존재한다. 즉 정보에 대한 정보를 보관하는건데,

MySQL 에서 일반적인 데이터에 대해 table을 생성하고 database를 형성하는데, 메타데이터를 종류별로 모아 table을 생성해 INFORMATION_SCHEMA라는 database를 형성한다.

Information_schema라는 데이터베이스 안에, 여러 테이블들이 포함되어 있는 것을 확인할 수 있다.

SQL injection에서 주로 사용할 테이블은 COLUMNS, TABLES, SCHEMATA라는 테이블이다

 


SQL Injection & INFORMATION_SCHEMA?

SQL Injection이 가능한 상황에서, 서버에 어떤 데이터베이스가 있는지 모를 때, 어떤 테이블이 있는지 모를 때, 컬럼명을 모를 때 INFORMATION_SCHEMA를 사용한다.

 

1. Database를 알아내고 싶으면,

SELECT schema_name from information_schema.schemata;

를 해주면 된다.

 

이는 SHOW databases; 와 동일한 결과를 가져오는데, 주로 select 문에 injection을 해주다보니 위의 쿼리를 사용하면 된다.

 

이 때, schema_name은, information_schemata라는 데이터베이스의 schemata라는 테이블의 컬럼을 의미하게 된다.

 

그렇다면 schemata라는 테이블은 어떤 정보들을 담고 있을까? 해서 확인해보니, schema_name은 이처럼 모두 해당 서버에 있는 데이터베이스 명이다.

 

 

 

2. Table을 알아내고 싶으면,

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

이는 Use 데이터베이스 명; Show tables; 한 결과와 동일하다.

이 때, table_schema와 table_name은 데이터베이스의 tables라는 테이블의 컬럼들을 의미하고, tables라는 테이블은 서버에 있는 database의 모든 table들에 대한 정보를 담고 있다.

 

table_name (테이블 명), table_schema (테이블이 어떤 데이터베이스에 있는지), table_rows (테이블의 row 개수) 등등이 포함돼 있는 테이블이다.

 

3. COLUMN을 알아내고 싶으면

1) 특정 database에 있는 column들 모두: table_schema로 database 지정

db2019100033라는 database에 battles라는 table이 존재하고 b-name이라는 column을 가짐

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

 

 

2) 특정 table에 있는 column들: table_name으로 table 지정

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

 

 

Columns라는 테이블에 서버 상 column들에 대한 정보를 모두 포함하고 있다.

해당 테이블에 table_schema라는 컬럼은 column이 어떤 database 상에 존재하고, table_name은 컬럼이 어떤 table 상에 존재하는지, column_name은 컬럼명을..보여준다. 그외에 column_type 등등이 있다.

 

4. 현재 사용중인 데이터베이스, MySQL 버전을 알고 싶다면

 

 

 

사실 공식 문서를 참고하는게 제일 좋다. 아예 INFORMATION_SCHEMA의 각 Table에 대한 정보를 자세하게 설명해준다.

https://dev.mysql.com/doc/refman/8.0/en/information-schema.html

 

MySQL :: MySQL 8.0 Reference Manual :: 26 INFORMATION_SCHEMA Tables

The world's most popular open source database

dev.mysql.com


사실 이것은 webhacking.kr의 chall29 를 풀기 위한 빌드업. (40점 짜리 문제. 사이트가 사실 리뉴얼 되면서 모든 문제 점수들이 1/10로 줄어들었다..ㅜ)  이렇게 파일을 업로드하는 창이 뜬다.

 

chall29

 

1. FLAG is in another table 이라는 문구

2. time, ip, file 이란 컬럼명과 테이블 형식으로 나와있다는 점 을 통해

 

이 문제는 SQL injection 문제임을 직감한다.!

 

파일을 업로드하면 filename에 전달해준 파일명이 POST 방식으로 전달된다.

upload success가 되면 time, ip, file 컬럼의 테이블 형식으로 data를 보여줌을 확인할 수 있다.

 

 

이때, 쿼리를 추정해보면.

  • 파일을 업로드 할때에는

INSERT into (테이블 명) values (time, ip, 'filename');

이런식일 것이고

 

  • 파일을 테이블 형식으로 보여줄 때에는

SELECT * from (테이블 명); 

이런식일 것을 추정할 수 있다.

 

이때 attack vector는 첫번째 경우 이므로 이에 맞게 페이로드를 짜보자.

 

 

 

 

쿼리를 확정하기 위해,

1. 컬럼 순서를 검증해야 했고, 2. 각 컬럼들이 따옴표가 필요한지 (문자열인지 숫자인지) 등을 따져야 했고,

이 문제의 경우는 3. ip 주소는 임의의 값이 아닌 내 컴퓨터 ip 주소여야 했었다.

 

대충 쿼리는 insert into (테이블 명) values ('filename', time, 'ip 주소') 인 것 같았으니,

이에 맞게 SQL injection 페이로드를 작성해보면,

 

aaaa', 1234, 'ip주소')# 를 보내보고, (결과가 확인이 제대로 될때까지 컬럼 순서들을 바꿔가면서 확인해보면 됨).

이는 결국 insert into (테이블 명) values ('aaaaa', 1234, 'ip 주소') 라는 쿼리가 실행되므로 결과가 잘 확인된다 

 

.

 

이제 database 명, table 명, column 명을 알아내야 한다.

이때 고려할 점은, select를 했을때 row는 하나만 반환이 돼야 이것을 filename에 넣어 결과를 확인할 수 있다. (여러개가 반환되면 upload error 가 뜬다)

 

따라서 limit 1을 시도 했는데, 이것은 table 가장 위에 있는 row 하나만 반환해서 문제였고,

결국 group_concat 함수를 사용해 반환값을 하나의 문자열로 연결하는 방식을 사용했다.

 

 

1. Database 명 알아내기

우선, 현재 사용중인 database를 알아내려면 database() 함수를 사용하면 되고,

SELECT group_concat(table_name) from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=database();

를 하면, 현재 사용중인 databse의 table 이름들을 보여준다. 

 

결과적으로 페이로드는, 앞서 보낸  insert into (테이블 명) values (aaaa', 1234, 'ip주소'), ((SELECT group_concat(TABLE_NAME) from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA= database()), 111111, 'ip 주소')# 

를 해주면, 결과에 aaaa라는 filename을 포함하는 row와, 현재 사용중인 database의 table 이름들을 보여주는 row가 추가적으로 insert 되었음을 확인 가능하다.

 

 

files와 flag_congratz라는 테이블이 존재한다.

 

 

2. Column 명 알아내기

flag_congratz라는 테이블에 플래그가 포함돼있는 거 같으니, 해당 테이블의 column들을 알아내보자.

마찬가지로 group_concat 함수를 사용해 컬럼명들을 하나의 문자열로 연결하면 된다.

SELECT group_ concat(COLUMN_NAME) from INFORMATION_SCHEMA.COLUMNS

where TABLE_NAME=’flag_congratz’ 를 활용해 위의 페이로드를 변형시켜준다.

flag라는 컬럼이 존재한다.

 

 

 

컬럼명과 테이블 명을 모두 알고 있으니,

SELECT flag from flag_congratz 쿼리로 페이로드를 변형시켜

플래그를 획득할 수 있다.

 

댓글