' or '1'='1

DATE : 2023/11/25(~26)

SQL Injection에서 DB에 저장된 모든 회원 정보를 꺼내오기 위해 사용했던

' or '1'='1

구문에 대해 고민해보는 시간이다. ~.~

SQL Injection을 실행할 초-간단 로그인 form을 만들어 활용하도록 하자.

이번 로그인 페이지와 연결되어 있는 member table에는 위와 같이 총 4명의 정보가 들어가 있다.

기본적으로 사용자가 자신의 정보를 입력하면

로그인 성공 여부와 함께 사용자의 정보를 출력해준다.

준비는 끝났으니, 바로 SQL injection을 수행해보자!!

우리의 목표는 DB에 있는 모든 정보를 출력하는 것이다.

[ case 1 ] : ' or '1'='1

제일 먼저 시도해볼 구문은 ' or '1'='1이다.

SQL injection을 해보기 전에 이 구문이 어떻게 동작하는 먼저 알아볼 필요가 있다.

ID : hanhxx' or '1' = '1

사용자가 다음과 같이 ID를 입력하면 로그인 과정에 따라 크게 두 가지 경우를 고려해볼 수 있다.

첫 번째 경우는 식별과 인증을 동시에 수행하는 경우이고

SELECT * FROM member WHERE id='user_id' and pass='user_pass'

두 번째 경우는 식별과 인증을 따로 수행하는 경우이다.

$res = SELECT * FROM member WHERE id='user_id'

if($user_padd == $res['pass']) {
    //success
} else {
    //fail
}

앞에서 이미 언급한 바와 같이 우리는 이 중에서도

식별과 인증을 동시에 수행하는 경우를 먼저 생각해볼 것이다.

[ Result 1 ]

ID : hanhxx' or '1' = '1
PASS : hanhxx1234

> SELECT * FROM member WHERE id='hanhxx' or '1'='1' and pass='hanhxx1234'

식별과 인증을 동시에 수행하는 경우, DB에서 실행되는 SQL은 위와 같다.

결과부터 말하면 이 구문으로는 DB의 모든 계정 정보를 얻을 수 없다.

id = 'hanhxx' or '1'='1' and pass='hanhxx1234'

이해를 위해 where문만 따로 보면, 현재 이 구문에서 사용된 논리 연산자는 총 2개로

or, and가 각 하나씩 사용된 걸 볼 수 있다.

이때 알아야 하는 점은, 논리 연산자 간의 연산 우선순위가 있다는 것이다.

우선 순위를 적용해 연산 과정을 따져보면

(1) '1' = '1' and pass = 'hanhxx1234' -> pass = 'hanhxx1234'

: and 연산에서 '1'='1'은 항등원으로 (1)은 결국 pass='hanhxx1234'와 동일하다.

(2) id = 'hanhxx' or password = 'hanhxx1234'

: (1) 과정을 거친 후, 만들어지는 query는 (2)와 같기 때문에

SQL의 결과로 id가 hanhxx이거나 password가 hanhxx1234인 row를 반환할 것이다.

이때, id가 hanhxx이거나 password가 hanhxx1234인 row는 동일한 row를 가리키기 때문에

결과적으로 hanhxx의 정보만 출력될 것을 예측할 수 있다.

[ Result 2 ]

ID : hanhxx' or '1' = '1' #
PASS : hanhxx1234

> SELECT * FROM member WHERE id='hanhxx' or '1'='1'#' and pass='hanhxx1234'

위에서 실행된 Query와 달리 이번에는 주석을 사용해보았다.

이런 경우에는 주석 이후의 내용이 모두 지워지기 때문에

id='hanhxx' or '1'='1'

where문의 내용은 위와 같게 된다.

전체적인 구문으로 이 query의 의미를 생각해보면

> SELECT * FROM member WHERE id='hanhxx' or '1'='1'

> SELECT * FROM member WHERE id='hanhxx' or true

id가 hanhxx이거나 True인 ROW의 정보를 조회하라는 의미가 된다.

따라서 이 구문은 DB에 있는 모든 ROW에 대해 참인 결과를 만들게 되고

결과적으로 모든 회원의 정보를 가져오게 된다.


이제 예측했던 결과가 나오는 지 실제로 SQL Injection을 수행해보자.

ID : hanhxx' or '1'='1
PASSWD : hanhxx1234

페이지에 제공되는 입력 칸에 ID & Password를 위와 같이 입력하면

hanhxx의 정보만 출력 되는 걸 볼 수 있다.

위에서 살펴본 [ Result 2 ]와 같은 상황임을 알 수 있다.

따라서 모든 회원의 정보를 얻기 위해서는

SELECT * FROM member WHERE id='hanhxx' or '1'='1' and pass='hanhxx1234'

and 연산자부터 SQL로 실행되지 못하도록 처리해야 한다. 이를 위해 우리는

ID : hanhxx' or '1'='1' #

> SELECT * FROM member WHERE id='hanhxx' or '1'='1' # and pass='hanhxx1234'
> SELECT * FROM member WHERE id='hanhxx' or '1'='1'

주석을 사용할 수 있다.

ID 맨 뒤에 주석을 넣게 되면 AND pass = 'hanhxx1234' 부분이 주석 처리되면서

SQL로써 기능을 잃게 된다.

따라서 이와 같이 ID를 입력하게 되면

password 조건을 확인하는 구문이 주석 처리되면서

위에서 살펴본 내용을 진행하면서 궁금했던 부분을 추가적으로 다루어볼까 한다.

첫 번째 궁금증은 굳이 굳이 굳이 ID hanhxx를 사용해야 할까?? 이다.

hanhxx' or '1'='1' #

ID 값에 ' or '1'='1' # 을 붙여 결국 모든 id에 대해 참인 조건을 만든다고 했으면

특정 User ID는 입력하지 않아도 되지 않을까 싶었다.

그래서 직접 확인해본 결과, 위와 같이 ID를 입력했을 때에도

로그인 성공은 물론, 모든 회원의 정보를 얻을 수 있었다.

그도 그럴 것이

ID : ' or '1'='1' #
PASSWD : 1234 (anything)

> SELECT * FROM member WHERE id = '' or '1'='1' # pass='1234'
> SELECT * FROM member WHERE id = '' or '1'='1'

결과적으로 실행되는 SQL은 ID가 ' ' 이거나 '1'='1'(참)인 경우의 ROW를 모두 가져오기 때문에

여기서 중요한 건,

입력된 User ID가 무엇이냐가 아니라 실행되는 SQL의 조건 결과가 어떻게 되느냐 이다.

두 번째 궁금증은, or 연산자로 모든 경우에 대해 결과가 true가 되도록 사용한

'1'='1' 

이 조건!! 굳이 굳이 굳이 이렇게 작성해야 하는 걸까??

여기서 '1'='1'이 사용된 이유

말 그대로 '1'은 '1'이다. 라는 동일한 피연산자를 비교함으로써 항상rue인 결과를 만들기 위함이다.

따라서 꼭 '1'='1'이 아닌 참을 의미하는 다른 형태의 값을 사용해보기로 했다.

EX) 
ID : ' or 1 #
ID : ' or 'a'='a' #
ID : ' or true #
ID : hanhxx' or 1 #
ID : hanhxx' or true #

예시로 나열한 SQL을 모두 실행해보면

위의 결과와 마찬가지로 모든 회원의 정보를 얻을 수 있다!!

이제 마지막 궁금증!!

로그인 과정에서 식별과 인증을 동시에 진행하는 경우, 주석 처리를 하지 않으면

pass와 관련된 조건이 and 연산으로 실행되면서

ID : hanhxx' or '1'='1'
PASSWORD : hanhxx1234

> SELECT * from member WHERE id='hanhxx' or '1'='1' and password='hanhxx1234'
> SELECT * from member WHERE id='hanhxx' or password='hanhxx1234'

id가 hanhxx이거나 pass가 hanhxx1234인 row의 결과를 출력하게 된다고 했다.

지금은 하나의 계정에 대한 id & password를 입력했기 때문에 hanhxx에 대한 정보만 출력 되었지만

만약 서로 다른 계정의 id & password를 입력한다면 어떻게 될까??

위와 같이 ID에는 or 연산자가 포함된 아이디를, PASS에는 MARIO의 비밀번호를 입력하게 되면

ID : hanhxx' or '1'='1
PASSWORD : jumpjump!!

> SELECT * FROM member WHERE id='hanhxx' or '1'='1' and pass='jumpjump!!'
> SELECT * FROM member WHERE id='hanhxx' or pass='jumpjump!!'

결과적으로 실행되는 구문은 (1)ID가 hanhxx이거나 (2)PASS가 jumpjump!!인 row를 찾아 정보를

출력하기 때문에

결과를 보면 hanhxx와 mario의 계정 정보가 출력된 것을 확인할 수 있다!


이렇게 해서 로그인 과정에서 식별 & 인증을 동시에 수행하는 경우,

' or '1'='1 형태의 query가 어떤 과정을 통해 결과를 이끌어내는지 알아보았다.

기억해둘 만한 정보를 정리해보면

(1) 식별 / 인증 동시 실행의 경우, and 연산자를 제거하기 위해 주석을 사용할 수 있다.

(2) 특정 User를 선별하는 게 아닌, 모든 User를 대상으로 하는 경우 적합하다.

(3) '1'='1'과 같이 참을 나타내는 값을 사용할 수 있다.

(4) 주석을 사용하지 않는 경우에는 서로 다른 사용자의 계정으로, 두 사람의 정보를 얻을 수 있다.

이와 같이 나열할 수 있을 듯하다!!

Last updated