What is SQL Injection? #Login Bypass

심야·2023년 7월 14일
0

모의해킹

목록 보기
25/47

What is SQL Injection?

SQL 질의문을 삽입하는 공격

데이터 추출, 인증 우회, 데이터 변조(삭제, 수정)

SQL Injection - Login Bypass

SQL Injection 취약점이 존재하면 아이디와 비밀번호를 몰라도 로그인이 가능하다.
어떻게 가능한 걸까? 모의 해킹에 사용하기 위해 개발 중인 게시판의 로그인 과정을 살펴보자.

form 에 아이디와 비밀번호를 입력하고 submit 하면 post 메소드로 아이디와 패스워드를 /hackthebox/loginbypass 경로에 전송한다. 이제 loginbypass 는 어떻게 아이디와 패스워드를 처리하는지 살펴보자.

아이디와 패스워드 파라미터를 전달 받은 DAO 객체는 DB에 아이디와 패스워드를 식별, 인증한다.
식별 및 인증에 성공했다면 DB에서 리턴한 VO 객체에 유저 계정이 존재하므로 세션을 생성한다.
만약 식별 및 인증에 실패했다면 vo 객체에 유저 계정이 존재하지 않으므로 다시 로그인 페이지로
리다이렉트 한다.
아래 코드는 DAO 객체가 DB에 존재하는 아이디, 패스워드와 유저가 입력한 아이디, 패스워드가
일치하는지 검증하는 로직이다.

입력받은 아이디와 패스워드를 아래 SQL 쿼리에 삽입 해 DB에 질의한다.

유저가 입력한 아이디와 패스워드가 일치하면 DB는 행을 리턴하며 그 결과는 아래와 같다.

SQL Injection 은 SQL질의문을 삽입해 데이터 변조, 추출, 인증 우회를 시도하는 공격 기법이다.

즉, 로그인 인증을 우회하려면 SQL Query 를 삽입해야 한다. 다양한 방법이 있지만 우선 가장 간단한 주석과 OR 연산자로 로그인 인증을 우회하겠다.

식별과 인증을 동시에 하는 로그인 과정

아래 SQL Query는 식별과 인증을 동시에 한다. 식별이란 많은 데이터 중에서 중복되지 않는 특정
데이터를 가려내는 것으로 로그인 과정에서는 유저가 입력한 아이디가 DB에 존재하는지를 의미
한다. 인증이란 식별한 아이디로 로그인을 시도하는 유저가 계정 주인이 맞는지 검증하는 과정이다.

로그인 우회 - 주석

아이디 inmo'-- 패스워드 1111 입력 후, 로그인을 시도한 결과 로그인이 성공한다.

로그인 우회 - OR 연산자

이번엔 아이디 inmo' or '1'='1 패스워드 1111 입력 후, 로그인을 시도한 결과 로그인이 성공한다.

로그인 우회 - 주석과 OR 연산자를 함께 사용

위에서는 아이디는 알지만 비밀번호를 모르는 상태로 가정하고 공격을 진행했다. 그런데 아이디와 비밀번호를 모르는 경우가 더 많을 것이다. 그런 경우에는 위 그림처럼 injection' or '1'='1' --

질의문으로 공격을 시도할 수 있다.

로그인 우회 - UNION

UNION 을 사용해 공격을 시도하겠다. 공격 Query 는 아래와 같다.

공격 시도 결과 아래와 같이 로그인에 성공하였다.

식별과 인증을 분리한 로그인 과정

식별과 인증을 동시에 했기 때문에 SQL Injection 취약점이 존재하는 것일까?
식별 후 인증을 진행하면 안전할까? 식별과 인증을 분리해도 취약점이 존재하는지 확인해보겠다.

아래 쿼리와 코드는 userId 가 존재하면 DB에 저장된 userId 비밀번호와 유저가 입력한 비밀번호가 일치하는지 확인하는 코드이다.

로그인 우회 - 주석과 OR 연산자

이번에도 주석과 OR 연산자를 사용해 공격을 시도하겠다.

아래 그림에서 보듯이 모두 로그인에 실패한다.

식별과 인증을 분리하니 주석과 OR 연산자 모두 로그인 우회에 실패했다.

로그인 우회 - UNION 연산자

이번에는 UNION 을 사용해 공격을 시도하겠다. 공격 Query 는 아래와 같다.

공격 시도 결과 아래와 같이 로그인에 성공하였다.

취약점 원리 파악

결론적으로 식별과 인증을 분리하거나 동시에 사용하거나 상관없이 취약점이 존재한다. 그럼 어떻게 인증을 우회하는걸까?

식별, 인증 동시에 사용하는 경우

Query는 식별과 인증을 동시에 진행한다. 이 Query 는 입력한 아이디와 비밀번호가 DB에 저장되어 있는 아이디, 비밀번호와 일치해야 한다. 그럼 주석, OR 연산자 그리고 UNION은 어떻게 인증을 우회한 걸까?

우선 SQL에서 ' 작은 따옴표는 문자열의 시작과 끝을 나타내는 의미를 가진다. SELECT * FROM users WHERE user_id='' AND password='' 쿼리에서 inmo' 문자열을 삽입해 의도적으로 문자열의 따옴표를 닫는다. 그럼 처음에 존재하던 ' 작은 따옴표가 덩그러니 남으며 최종 Query는 아래와
같다.

SELECT * FROM users WHERE user_id='inmo' ' AND password=''

이렇게 Query 를 작성하고 DB에 질의하면 문법 에러가 발생한다. 그래서 닫히지 않은 작은 따옴표를 주석으로 제거하거나 작은 따옴표를 하나 더 삽입하여 따옴표를 맞춰야 한다.

주석

  • MySQL 주석 inmo’ #
  • Oracle 주석 inmo’ --

닫히지 않은 작은 따옴표와 뒷 문장을 주석 처리 해 무시한다. 주석을 삽입한 Query는 아래와 같다.

 SELECT * FROM users WHERE user_id='inmo' -- ' AND password=''

패스워드를 질의하는 AND password='' 부분은 무시되고 DB에 질의하는 최종 쿼리는 아래와 같다.

 SELECT * FROM users WHERE user_id='inmo'

따라서 ID만 일치하면 로그인에 성공한다.

OR 연산자

OR 연산자는 연산자 기준으로 앞, 뒤 조건 중 1개 이상이 참이면 최종 결과가 참이다.
예를 들어 1=1 or 1=2 연산은 참이다. 1=1은 참 / 1=2는 거짓이지만 1=1 연산이 참이므로 OR 연산 결과는 참이 된다. OR '1'='1 문자열 삽입 쿼리는 아래와 같다.

 SELECT * FROM users WHERE user_id='inmo' OR '1'='1' AND password='1111'

SQL은 OR 연산자보다 AND 연산자 우선순위가 더 높다. 따라서 '1'='1' AND password='1111' 연산이 먼저 수행된다.

 SELECT * FROM users WHERE user_id='inmo' OR ('1'='1' AND password='1111')

'1'='1' 조건은 논리 구조에서 참을 반환하지만, password='1111' 같은 조건은 저장된 실제 데이터를 기반으로 평가하므로 DBMS에 질의하기 전까지는 참/거짓을 판단할 수 없다. 그래서 ('1'='1' AND password='1111') 구문은 DBMS에서 실시간으로 true and password='1111' 구문으로 변경되고 true and password='1111' 구문은 다시 password='1111' 조건만 남는다.

그래서 결국, DBMS에서 취약점 익스플로잇 쿼리는 아래와 같다.

SELECT * FROM users WHERE user_id='inmo' or password='1111'

users 테이블에 아이디가 inmo 또는 비밀번호가 1111인 행이 존재할 경우, 해당 행을 반환 즉, 아이디가 inmo인 행이 있다면 비밀번호 유효성 검증없이 로그인에 성공한다.

OR 연산자와 주석을 같이 사용

inmo' OR '1'='1' -- 문자열을 삽입하면 Query 는 아래와 같다.

 SELECT * FROM users WHERE user_id='inmo' OR '1'='1' -- AND password='1111'

우선 주석으로 인해 패스워드 부분은 무시되고 SELECT * FROM users WHERE user_id='inmo' 조건과 '1'='1' 조건을 OR 연산한다. 그런데 SELECT * FROM users WHERE user_id='inmo' 조건이 참인지 거짓인지 상관없이 '1'='1' 조건이 항상 참이기 때문에 테이블의 모든 행을 반환하고 로그인에 성공한다.

식별, 인증을 분리하는 경우

주석, OR 연산자

userId가 DB에 존재하면 userId 비밀번호와 유저가 입력한 비밀번호가 일치하는지 검증한다.

식별과 인증을 동시에 할 때는 주석과 OR 연산자로 패스워드 검증 부분을 우회했지만 인증을 분리해 진행하므로 주석과 OR 연산자 사용이 무의미하다.

UNION 연산자

UNION은 두 SQL 질의문 결과를 합친 연산자이다. SELECT * FROM users WHERE user_id='inmo' UNION SELECT '1','1','1','1', SYSDATE FROM dual 질의문은 USERS 테이블 각각의 컬럼에 SELECT * FROM users WHERE user_id='inmo'SELECT '1','1','1','1', SYSDATE FROM dual 질의문의 합집합을
출력한다.

UNION 연산자의 이러한 특징을 활용한 공격을 UNION SQL Injection 이라 부른다. 따라서 injection union select '1','1','1','1',sysdate from dual -- 질의문으로 UNION SQL Injection 을 시도하면 USERS 테이블에 새로운 행이 생성되며 생성된 행의 비밀번호가 1이기 때문에 비밀번호로 1을 입력하면 로그인 인증을 우회할 수 있다.

TIP

게시판 검색

sotingAd=date

sotingAd=, (case when (조건) then (참인 경우 = 1) else (거짓인 경우 = 1/0) end)

order by 1, (case when ascii(substr((select user from dual), 1, 1))=0 then 1 else (1/0) end)

⇒ (1/0) 부분에서 에러 발생

Time based SQLi

sotingAd=ASC; if substring((select user_name()),1,1)=’a’ waitfor delay ‘0:0:1’

⇒ 첫 글자가 a 라면 1초 멈춘다.

UNION SQLi

select 1, concat(), 3, 4, 5 from member ⇒ concat() 으로 컬럼 하나에 모든 데이터 출력 가능하다.

profile
하루하루 성실하게, 인생 전체는 되는대로.

0개의 댓글