
문제를 풀기 위해서는 database version string를 출력하면 된다고 한다. 문제 제목에서 알 수 있듯이 오라클DB를 사용하고 있으므로, 오라클 DB 버전을 알아내는 쿼리를 입력하자.
SELECT banner FROM v$version SELECT version FROM v$instance
두 명령어의 차이는 아래와 같다고 한다.
구분 | v$version.banner | v$instance.version |
목적 | 제품 및 구성요소 버전 설명 | 현재 인스턴스의 DB 버전 |
결과 행 수 | 여러 행 가능 | 일반적으로 1행 |
결과 형태 | 설명 문자열 | 버전 번호 |
에디션 표시 | Enterprise Edition 등 표시 가능 | 표시하지 않음 |
패치 정보 | BANNER_FULL에서 표시될 수 있음 | 보통 기본 릴리스만 표시 |
우리는 버전만 뽑으면 되므로 우선
SELECT version FROM v$instance 를 사용하자.URL: /filter?category='%20UNION%20SELECT%20version%20FROM%20v%24version%20-- SQL: ' UNION SELECT version FROM v$version --

컬럼 개수와 문자열 컬럼 확인
하지만 오류가 났다. 왜 그럴까? 이는
UNION 연산자의 특징을 먼저 이해해야 한다. UNION은 SQLi에서 정말 많이 사용되는 구문이지만, 본래 목적은 두 테이블의 합집합 연산이다. 정확히 말하면 두 SELECT 문의 결과 집합을 세로 방향으로 합치는 집합 연산자다.예를 들어 첫 번째 조회 결과가 다음과 같다고 하자.
name | description |
Product A | Description A |
Product B | Description B |
여기에
UNION SELECT로 새로운 행을 추가하려면 두 번째 SELECT 역시 첫 번째 SELECT와 동일한 개수의 컬럼을 반환해야 한다.SELECT name, description FROM products UNION SELECT 'abc', 'def' FROM dual;
첫 번째 쿼리가
name과 description이라는 두 개의 컬럼을 반환하므로, UNION 뒤의 쿼리도 'abc'와 'def'라는 두 개의 컬럼을 반환한다.그 결과는 다음과 같은 형태가 된다.
name | description |
Product A | Description A |
Product B | Description B |
abc | def |
반대로 다음과 같이 두 쿼리가 반환하는 컬럼의 개수가 다르면 결과를 합칠 수 없다.
SELECT name, description FROM products UNION SELECT version FROM v$instance;
각 쿼리가 반환하는 컬럼의 구조를 비교하면 다음과 같다.
쿼리 | 첫 번째 컬럼 | 두 번째 컬럼 |
원본 SELECT | name | description |
UNION SELECT | version | 없음 |
첫 번째
SELECT 문은 두 개의 컬럼을 반환하지만, 두 번째 SELECT 문은 version이라는 한 개의 컬럼만 반환한다. 각 행의 구조가 다르기 때문에 Oracle은 두 결과 집합을 합칠 수 없으며 오류를 발생시킨다.즉, 다음 쿼리 자체가 잘못된 것은 아니다.
SELECT version FROM v$instance;
이 쿼리를 독립적으로 실행하면 현재 Oracle 인스턴스의 버전 번호가 정상적으로 반환된다.
문제는 SQL Injection을 통해 기존 쿼리에
UNION으로 연결했을 때 발생한다. 애플리케이션이 내부적으로 실행하는 원본 쿼리가 두 개의 컬럼을 반환한다면, 공격자가 삽입하는 UNION SELECT 역시 두 개의 컬럼을 반환해야 한다.애플리케이션의 원본 쿼리는 대략 다음과 같은 구조라고 추측할 수 있다.
SELECT name, description FROM products WHERE category = '<사용자 입력>';
여기에 처음 작성한 payload를 삽입하면 최종 쿼리는 대략 다음과 같은 형태가 된다.
SELECT name, description FROM products WHERE category = '' UNION SELECT version FROM v$instance --';
원본 쿼리는 두 개의 컬럼을 반환하지만 삽입한 쿼리는 한 개의 컬럼만 반환한다. 따라서 처음 작성한 payload에서 오류가 발생한 것이다.
따라서,
UNION SELECT를 이용하려면 먼저 원본 쿼리가 반환하는 컬럼의 개수를 알아내야 한다. 또한 조회한 데이터베이스 버전은 문자열이므로, 어느 컬럼에 문자열 데이터를 삽입할 수 있는지도 확인해야 한다.다음 payload를 사용하자.
/filter?category='%20UNION%20SELECT%20'abc'%2c%20'def'%20FROM%20dual--
' UNION SELECT 'abc', 'def' FROM dual--
Oracle에서는 특정 테이블의 데이터를 조회하지 않고 상수나 함수의 결과만 반환할 때
dual 테이블을 사용할 수 있다.SELECT 'abc', 'def' FROM dual;
이 쿼리는 다음과 같이 한 행과 두 개의 문자열 컬럼을 반환한다.
첫 번째 컬럼 | 두 번째 컬럼 |
abc | def |
payload가 삽입되면 애플리케이션에서 실행되는 쿼리는 대략 다음과 같다.
SELECT name, description FROM products WHERE category = '' UNION SELECT 'abc', 'def' FROM dual --';

두 컬럼 모두 문자열 데이터를 처리할 수 있으며, 두 컬럼의 값이 모두 화면에 표시된다는 것도 확인할 수 있다.
데이터베이스 버전 출력
원본 쿼리가 두 개의 컬럼을 반환한다는 사실을 확인했으므로,
v$instance.version을 조회하는 쿼리 역시 두 개의 컬럼을 반환하도록 수정해야 한다.SELECT version, NULL FROM v$instance;
실제로 필요한 값은
version 하나뿐이다. 하지만 원본 쿼리의 컬럼 개수와 맞추기 위해 두 번째 컬럼에 NULL을 추가한다.최종 payload는 다음과 같다.
/filter?category='%20UNION%20SELECT%20version%2c%20NULL%20FROM%20v%24instance--
' UNION SELECT version, NULL FROM v$instance --
하지만 동일한 오류가 발생했다.

오류가 발생한 원인은 애플리케이션이 사용하는 데이터베이스 계정의 조회 권한 때문으로 보인다.
V$INSTANCE는 일반 테이블이 아니라 Oracle의 시스템 정보를 제공하는 동적 성능 뷰다. V$INSTANCE라는 이름을 알고 있더라도, 실제 객체를 조회할 수 있는 권한이 없다면 쿼리가 실패할 수 있다.참고문헌
Database Concepts
The central set of read-only reference tables and views of each Oracle database is known collectively as the data dictionary. The dynamic performance views are special views that are continuously updated while a database is open and in use.
SQL Language Reference
Synonyms provide both data independence and location transparency. Synonyms permit applications to function without modification regardless of which user owns the table or view and regardless of which database holds the table or view. However, synonyms are not a substitute for privileges on database objects. Appropriate privileges must be granted to a user before the user can use the synonym.
앞에서
'abc', 'def'를 사용한 쿼리는 정상적으로 실행되었으므로, UNION의 컬럼 개수나 문자열 데이터 타입 때문에 발생한 오류는 아니다. 또한 V$INSTANCE에는 실제로 VERSION 컬럼이 존재하므로 쿼리 문법 자체도 올바르다.다만 실습 화면에는 구체적인 Oracle 오류 코드가 표시되지 않으므로, 조회 권한 문제가 정확한 원인이라고 확정할 수는 없다. 현재 상황에서는
V$INSTANCE에 대한 접근 권한이 제한되어 있을 가능성이 가장 높다고 추측할 수 있다.Oracle에서는 데이터베이스 버전을 확인할 수 있는 다른 동적 성능 뷰로
V$VERSION도 제공한다. 따라서 이번에는 V$INSTANCE.VERSION 대신 V$VERSION.BANNER를 사용해 보자./filter?category=%27%20UNION%20SELECT%20banner%2c%20NULL%20FROM%20v%24version%20--
' UNION SELECT banner, NULL FROM v$version --
정상적으로 실행되면 상품 정보가 표시되던 위치에 다음과 같은 Oracle 버전 번호가 출력된다.


