LOS 풀 때마다 까먹어서 정리하려고 이 글을 씁니다.
추가로 이글은 SQL INJECTION 공격 자동화 기법을 기반으로 작성했습니다.
일단은 간단하게 SQL Injection에 관해서 얘기를 하자면
웹페이지에서 sql 쿼리를 통해서 데이터베이스에 있는 데이터들을 얻는데,
이를 악용을 하여서 sql 쿼리를 조작을 하여서 공격자가 원하는 값들을 얻을 수 있게 하는 것이다.
SQL Injection 공격 종류
Union based SQL Injection
Error based SQL Injection
Blind based SQL Injection
Time based SQL Injection
이와 같은 종류의 인젝션이 있다.
해당 글에서는 Union 기반의 인젝션에 대해서 작성을 할 것이고,
나머지는 이 후의 글에서 다시 다루겠다.
What is UNION?
UNION 은 sql 쿼리에 있는 연산자 중 하나이며,
이는 SELECT 문에서 검색된 결과를 단일 집합으로 결합해주는 연산자이다.
사용을 위해서는 UNION을 통한 결합된 집합들은 같은 구조를 가져야 한다.
이는 같은 수의 열을 가져야 하며, 데이터 형식이 비슷해야 한다.
추가로 UNION에는 두 가지의 종류가 있는데,
UNION ALL은 중복을 포함해서 결과를 나타내 주고, UNION은 중복을 제거하고 결과를 나타낸다고 생각을 하면 된다.
예시로
SELECT 'ho' UNION SELECT '9';와 같이 입력을 하게 되면,
---------------------------------------Execute time: [2022/04/19 16:53:41]---------------------------------------
SQL > SELECT 'ho' UNION SELECT '9';
ho
-----
ho
9
위와 같이 결괏값이 결합되어서 나온다.
.
.
.
.
UNION Based SQL Injection
DataType에 따른 Injection
SQL 인젝션에서 데이터 타입에 따라서 공격 쿼리가 주입이 될 수도 있고 안될 수도 있다.
예시로 Integer 형태로 변수가 선언이 되어있으면 문자열 형식의 데이터가 삽입이 불가능하다.
하지만 이를 통해서 데이터 타입을 대략 유추가 가능하기도 하다.
1. 숫자형 데이터
/member_id.php?memid=1
숫자형 데이터는 말 그대로 파라미터를 통해서 숫자를 받는 것을 뜻한다.
기본적으로 sql 인젝션이 존재하는지 확인하는 법은 '(Single Quote)를 넣어보면 된다.
이와 같이 DB의 에러 메시지가 뜰 것이다.
실제 사이트 같은 경우는 이러한 에러 메시지도 뜨지 않게 통제를 했을 것이다.
이런 에러 메시지를 통해서 파라미터의 값이 sql 구문에 인젝션 된 것을 알 수 있기 때문이다.
해당 에러 메시지를 보면 near '''을 볼 수가 있는데,
대략 가늠해보면 파라미터를 주입할 수 있는 곳이 구문의 거의 끝부분이라는 것을 알 수가 있다.
공격을 하기 앞서서 사전 준비를 해야 한다.
처음에 UNION에 관해서 설명을 할 때,
UNION 구문은 앞의 SELECT절과 열의 개수가 같아야 하며, 데이터 형식이 비슷해야 한다고 했다.
그러므로 열의 개수와 데이터 타입을 알아봐야 한다.
열의 개수는 order by로 데이터 타입은 에러 기반으로 확인이 가능하다.
COLUMN의 개수
이는 ORDER BY 절을 통해서 알 수가 있다.
ORDER BY 절은 데이터를 정렬(내림차순, 오름차순)을 하기 위한 명령어이다.
해당 명령어는 ORDER BY [column_name | position] [ASC | DESC]와 같이 사용을 할 수가 있다.
뒤에 붙는 ASC는 오름차순 DESC는 내림차순이며 명시를 하지 않으면 자동적으로 오름차순으로 정렬이 된다.
이를 통해서 열의 개수를 어떻게 아느냐...
ORDER BY를 했는데 오류 메시지가 뜨면 해당 열이 존재하지 않는 것이고
오류 메시지가 뜨지 않으면 해당 열이 존재하는 식으로 열의 개수를 찾는 것이다.
/member_id.php?memid=1+order+by+20
/member_id.php?memid=1+order+by+19
이와 같은 과정을 통해서 열의 개수를 알 수가 있다.
DataType 확인하기
이제는 DataType을 확인을 해야 한다.
SQL 쿼리에서는 데이터 타입에 상관없는 null문자를 넣어서 확인을 할 수가 있다.
/member_id.php?memid=1+UNION+SELECT+null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null+FROM+dual%23
와 같이 입력을 하면 null 데이터로 인하여 아무 값도 출력이 되지 않을 것이다.
데이터를 추가적으로 얻기 위해서는 위에 나온 값들이 어떤 데이터 형식을 갖고 있는지
몇 번째 열인 지도 확인이 필요하다.
해당 데이터베이스는 MySQL 기반으로써 DataType의 변화가 자동적으로 된다.
하지만 뒤에 나올 Oracle 기반의 데이터베이스는 자동적으로 변화가 되지 않는다.
(※이는 뒤에서 설명)
그러므로
/member_id.php?memid=1+UNION+SELECT+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19+FROM+dual%23
와 같이 작성을 해도 문자열로 되어있는 데이터에는 문자열로 데이터 타입이 자동적으로 변환돼서 들어간다.
추가적으로 데이터 타입은 앞 행의 결과를 보고 대략 예측이 가능하다.
Title 위치에 스키마 정보 조회를 통해서 테이블 정보들을 확인하겠다.
/member_id.php?memid=0+UNION+SELECT+1,2,3,table_name,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19+FROM+information_schema.tables%23
2. 문자형 데이터
문자형 데이터는 숫자형 변수와 다른 점이 싱글 쿼터 사이에 대입이 된다는 것이다.
이로 인해서 값을 주입을 할 때는 싱글 쿼터에 쌍을 맞춰서 문제가 없도록 해야 한다.
/member_name.php?memname=Andrew
파라미터를 문자열로 받는다.
위에서 했던 것과 동일하게 싱글 쿼터를 삽입을 해보면 에러 문구가 뜬다.
limit 10 부분에서 에러가 난 것으로 보아서 구문의 끝부분에 파라미터의 값이 들어가는 곳이 있는 것 같다.
열의 개수와 데이터 타입은 아까 전과 동일하므로 생략..
데이터 타입에서 중요한 싱글 쿼터 쌍을 맞추는 데는 두 가지 방법이 있는데,
아래와 같이 코딩된 싱글 쿼터를 건들지 않고 쌍으로 맞추는 방법과
/member_name.php?memname='and+1=2+union+select+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19+from+dual+where+1+or''='
주석을 통해서 쌍을 유지하는 방법이 있다.
주석은 #을 이용을 했고, URL 인코딩을 통해서 %23으로 기입이 가능하다.
/member_name.php?memname='and+1=2+union+select+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19+from+dual%23
개인적으로는 주석을 통해서 싱글쿼터 개수를 맞춰주는 것을 더 선호한다...
.
.
.
추가적으로 나머지 과정은 위와 동일하므로 생략..
3. 날짜형 데이터
날짜형 데이터는 oracle 데이터베이스를 통해서 설명을 해보겠다.
oracle 데이터베이스는 다른 데이터베이스들에 비해서 형 변환이 자유롭지 못하다고 위에서 설명을 했다.
확인을 통해서 알아보자.
In MySQL
---------------------------------------Execute time: [2022/04/19 18:38:21]---------------------------------------
SQL > SELECT now() UNION SELECT 'ho9';
now()
-------------------
2022-04-18 08:26:30
ho9
2 row(s) fetched.
In Oracle
==========================[Start Time : 2022/04/19 18:37:21]==========================
SQL > SELECT sysdate from dual UNION SELECT 'ho9' from dual
접속 종료
Error code: 17008
SQL state: 08003
이와 같이 데이터 변형이 자유롭지 못한 것을 알 수가 있다.
그러므로 쿼리문을 주입을 할 때도 형 변환에 주의를 하면서 작성을 해야 한다.
.
.
.
meminfo_date.jsp?date=2003-06-17
위와 같이 날짜를 입력을 받으면 해당 값이 HireDate와 일치하면 데이터 값을 보여주는 것을 알 수가 있다.
위의 다른 예제들과 동일하게 싱글 쿼터를 삽입하면 아래와 같이 오류가 뜨게 될 것이다.
에러 페이지를 확인하게 되면 싱글 쿼터로 에러가 난 것을 알 수가 있으며
입력을 받은 날짜는 to_date함수를 통해서 YYYY-MM-DD 형식으로 변환이 된다.
to_date함수를 방해하지 않으면서 그리고 싱글 쿼터의 쌍을 유지하면서 구문을 작성해야 한다.
meminfo_date.jsp?date=2003-06-17','YYYY-MM-DD')--
주석처리를 통해서 기존의 잔여 구문을 통한 에러가 발생하지 않게 했다.
열의 개수를 구하는 방법은 위와 동일하다.
하지만 몇 번째 열인지 확인을 하는 방법은 하나씩 대입을 해보는 수밖에 없다...
이전과 같이
union+select+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19+from+dual%23
이와 같은 식으로 값을 넣게 되면 자유롭지 못한 형의 변환으로 에러가 날 것이다.
이러한 점은 주의를 해야 한다
+
문자형과 숫자형의 값을 넣었을 때 에러가 난다면
날짜형 데이터일 가능성이 크다.
4. 스키마 객체 데이터
스키마 객체 데이터는 파라미터로 받은 값이 sql쿼리의 테이블 또는 칼럼으로 사용될 때 발생한다.
WHERE 절로 사용되는 경우
meminfo_col.jsp?cname=mem_id&empid=100
위와 같은 페이지가 있다.
싱글 쿼터를 통해서 해당 페이지의 에러 메시지를 확인해보겠다.
에러 메시지를 통해서 쿼리 구문을 확인을 할 수가 있다.
쿼리 구문을 확인을 하면 cname이 칼럼으로 사용되는 것을 알 수가 있지만
이와 같은 에러 메시지가 뜨지 않게 설정을 해놓으면 다른 방법으로 찾아야 한다.
방법 중에 하나는 참 명제, 거짓 명제를 사용하는 것이다.
참 명제, 거짓 명제 두 가지를 주입을 통해서 해당 파라미터가 칼럼으로 사용되는지 확인을 할 수 있다.
참 명제
meminfo_col.jsp?cname=1=1+and+mem_id&empid=100
거짓 명제
meminfo_col.jsp?cname=1=2+and+mem_id&empid=100
FROM 절로 사용되는 경우
meminfo_tab.jsp?tname=mem_id&empid=100
파라미터를 통해서 from절의 테이블 명으로 사용되는 경우에는
sql 쿼리 뒤쪽을 모두 주석으로 날려주면
모든 데이터를 출력이 된다.
... 요고는 쉬우니까 예시는 건너뛰겠다.
끝