티스토리 뷰

반응형

이번 세션은 SQL Injection 중 UNION Injection과 Blind Injection에 대한 내용이다.

 

첫 번째 퀴즈는 UNION Injection을 이용해서 유저의 패스워드 정보를 탈취하는 퀴즈인데, 

친절하게도 두 개의 테이블에 대한 정보를 제공해준다.

 

 

SQL Injection이 발생하는지 확인하기 위해 쿼리 에러가 발생할 수 있도록 입력값을 구성해서 전달해보니, 에러가 발생하는 것을 알 수 있었다. 이를 통해 SQL Injection이 가능할 수 있다는 것을 알 수 있다.

 

특히 에러 메시지를 통해 실제 실행되는 쿼리 구문이 무엇인지 까지 전달해주고 있어, SQL Injection에 더욱 도움이 되고 있는 상태이다.

 

UNION SQL Injection을 할 때에는 테이블명, 필드명을 알아야 할 뿐 아니라, 기존 쿼리로 조회되는 필드 갯수도 맞춰야 하므로, 일반적으로 Blind SQL Injection 등을 이용해서 테이블, 필드명을 확인하고, Union 구문에서는 null을 반복적으로 추가해가면서 실제로 내가 확인하고 싶은 데이터 필드를 확인하게 된다. 그런데 여기서는 테이블 정보를 알려주고 있고, 에러 메시지를 통해 몇 개의 필드가 사용되고 있는지 알 수 있기 때문에, 반복적인 노가다는 패스할 수 있었다. 지금까지 내용을 기반으로 쿼리를 작성하여 전달하면 전체 유저의 패스워드 정보를 확인할 수 있다.

a' or '1'='1' union select userid, user_name, password, NULL, NULL, NULL, NULL from user_system_data where 'a'='a

 

그럼 이제 문제에서 이야기 한 Dave의 계정정보를 확인해서 제출하면 이번 퀴즈도 끝이다.

 

 

코드에서도 알 수 있듯이 입력값을 검증없이 쿼리문에 문자열 결합으로 사용하고 있다.

이는 SQL Injection 발생할 있는 대표적인 사례이며, Exception 등이 발생했을 실제 사용되는 query 응답값에 함께 전달해주고 있어, SQL Injection 쉽게 있는 힌트를 제공하게 된다.

src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java

 

SQL Injection 발생하지 않도록 하기 위해서는 입력값을 파라미터화 바인딩을 하여 쿼리문으로 인식되지 않도록 구성해야 하며, 만약 문자열 결합을 반드시 해야 경우에는, 입력값에 쿼리로 인식할 있는 특수기호나 예약어가 포함되어 있는지 검증 사용되어야 한다. 또한 Exception 등의 에러를 처리하는 과정에서 쿼리 등의 중요정보가 포함되지 않도록 사용자 오류 메시지를 구성하여 전달하도록 코드를 구성해야 한다.

 

 

다음은 Blind SQL Injection 이다. 실제 진단을 할 때에는 Blind SQL Injection을 많이 사용한다. 실제 공격이 통한다는 것을 빠르고 간단히 확인할 수 있기 때문이다. 하지만 이 방법으로 실제 데이터를 탈취까지 하려면 많은 노가다가 필요하기 때문에 스크립트 작성까지 진행이 필요하다.

 

이번 질문은 Tom이라는 유저의 계정으로 로그인하라는 것이다.

 

정보가 너무 적은데, 우선 Tom의 계정명이 무엇인지 부터 확인해보아야 한다. (대략적으로 tom 부터 시작한다.)

몇 번의 SQL Error을 발생시켜 보았으나 SQL Injection이 발생하지 않는 것으로 보인다. REGISTER 메뉴가 있으니 이번에는 여기를 이용해 본다. 일반적으로 회원등록 시 기존에 계정명이 사용되고 있는지 확인하기 때문에, 이 기능을 통해 Tom 계정명은 tom 것을 확인할 있다.

 

 

이제 본격적으로 Blind SQL Injection을 시도해본다. 입력한 계정명이 조회되는 쿼리가 SQL Injection이 가능하지 않을까 싶어 Blind SQL Injection 구문을 추가해본다. 각 메시지를 확인해보았을 때 username 존재하지 않으면 계정을 생성하고, 특수기호를 삽입하면 에러가 발생하는 것을 보았을 . SQL Injection 가능성이 존재할 것으로 보인다.

tom11 계정명 사용 시 계정생성 완료 메시지 전달
tom 이라는 계정명을 사용 시 이미 존재하는 계정명이 사용되었다는 에러메시지 전달
tom' 입력 시 에러발생했다는 메시지 전달

 

 

그럼 본격적으로 Blind SQL Injection을 시도해본다.

조회 조건 뒤에 TRUE구문을 and로 함께 전달해보면, 이미 계정이 존재한다는 메시지를 전달한다.

이는 tom이라는 계정이 이미 존재한다는 것을 앞에서 확인했기 때문에, TRUE and TRUE로 조회가 되었다는 것을 알 수 있다.

또한 이번에는 FALSE 구문을 and로 함께 전달해보면 새로운 계정이 생성되었다는 메시지를 전달한다.

이를 통해 계정명을 이용하면 SQL Injection을 이용하여 정보를 탈취하거나 인증우회가 가능할 수 있다는 것을 의미한다.

 

 

그럼 이제 본격적으로 tom의 계정정보를 탈취해본다. 실제 패스워드에 대한 입력 파라미터 명을 보았을 때, 테이블에 저장되는 패스워드의 필드명은 password가 높은 확률로 사용될 것이다. (물론 어떤 필드인지 확인하기 위해 여러번의 Blind SQL Injection을 시도해야 하지만, 여기서는 시간을 줄이기 위해 몇가지 예측을 함께 해본다.) 

다행히 Password 필드의 길이를 체크하는 구문을 추가해보니 정상적으로 동작하는 것을 알 수 있었다.

 

 

그럼 이제 본격적으로 Intruder 등을 이용해서 실제 길이를 체크해본다.

23이 들어갔을 때부터 응답길이가 달라지고, 실제 메시지도 예측한 것처럼 나온다. 이를 통해 패스워드의 길이는 23자리인 것을 알 수 있다.

 

 

다음은 실제 23자리의 값을 찾는 것이다. 가장 고전적인 방식은 한자리 씩 substring 한 뒤 ASCII로 변환하여 값을 찾는 것이다.

역시 Intruder를 이용해서 한자리 씩 찾아본다.

 

첫 번째 문자열의 ASCIISMS 116이므로 소문자 ’t’이다.

이러한 방법을 사용하여 23자리르 모두 반복하면 패스워드를 알 수 있다.

 

 

이제 각 소스코드를 확인해보자.

로그인에 사용되는 소스코드를 확인해보면 문자열 결합없이 PreparedStatement 이용하여 입력값을 파라미터화 바인드 처리를 하고 있어, SQL Injection 불가능했다는 것을 있다.

src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java

 

반면에 회원가입을 처리하는 소스코드를 보면 입력받은 ID 이미 존재하는지 확인하기 위해 사용되는 쿼리가 문자열 결합을 통해 진행되고 있어 쿼리변조가 가능했다는 것을 있다.

src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java

 

반응형
댓글
반응형
최근에 올라온 글
Total
Today
Yesterday