Always-Try(정보보안 및 일상)

써니나타스 - Web Challenges - 22번 (블라인드 SQL 인젝션) 본문

Pen Test

써니나타스 - Web Challenges - 22번 (블라인드 SQL 인젝션)

Always-Try 2021. 1. 31. 21:32

#1. 22번

1-1. 문제

 

1-2. 풀이

개발자도구에서 힌트를 확인할 수 있다. 

guest/guest로 로그인하고, 최종적으로는 admin 계정의 비밀번호를 알아내는 것이 목표인 것으로 보인다.

그리고 문제 화면을 보면 여러가지 입력 값들이 필터링 되고 있느 것을 알 수 있다.

 

일단 로그인 시도 시, 어떤식으로 메시지가 나오는지 확인해보자.

아무 계정이나 입력하면, False 라는 메시지가 나온다.

 

힌트에 있는 guset/guest 를 입력하면 OK guest 라는 메시지가 나온다.

비밀번호에 필터링 되고 있는 문자열들을 넣어보면 No hack 라는 메시지가 나오고, 요청은 GET 방식으로 전달되고 있다.

추가로 몇가지 더 테스트해본 결과, pw에 아무것도 입력하지 않으면 동작하지 않고, ' 문자열은 필터링 되고 있지 않다. 이것을 기반으로 블라인드 인젝션을 시도해보도록 하자. 블라인드 인젝션은 하나씩 단계별로 진행하는 스무고개 같은 것이므로 시도할 법한 내용들을 순서대로 나열한다. 

 

힌트에 admin 계정이 존재한다는 것이 나오지만, 블라인드 인젝션 참/거짓 구문을 통해 admin 계정이 진짜 존재하는지 확인해보도록 하자. (위 테스트 결과 참일 경우, OK 계정명 이 나올 것이고 거짓일 경우 False가 출력될 것이다.)

 

1번째 시도

1' AND 1=1#

뭔가가 필터링 되고 있다. = 또는 # 일텐데, 테스트해보니 #이 필터링 되고 있다. 그렇다면 또 다른 주석 문자일 -- 를 이용해보자.

 

2번째 시도 

1' AND 1=1--

False가 출력된다. 더이상 필터링 되는 것은 없다. 2번째 시도의 결과에서 얻을 수 있는 것은 1이라는 계정은 존재하지 않는다는 것이다. 계정명을 모른다면 몇가지 더 시도해봐야 겠지만, 우리는 힌트에서 계정명을 알고 있으니 확인 차 3번째 시도를 통해 admin 계정이 존재하는지 확인해보자.

 

3번째 시도

admin' AND 1=1--

역시 admin 계정이 존재한다. 

계정의 존재는 알았고, 이제 패스워드를 찾아보자. 패스워드를 찾는 순서는 어떻게 해야될까?

?

?

?

일단 패스워드가 몇글자로 이루어져있느지 알 수 있으면 훨씬 수월할 것이다. 위 구문들을 조금 변경하여 확인해보자.

mysql의 경우, length함수를 통해 문자열의 길이를 확인할 수 있다. (단, 바이트 단위로 리턴되므로 한글 테스트 시 주의)

length 함수를 통해 비밀번호가 몇글자인지 비교해보려한다.

 

admin' AND length(비밀번호 컬럼) > 10 -- 와 비슷하게 진행할 예정인데, 우리는 비밀번호 컬럼명을 모른다.

보통 password 나 pw 이지만, 정확한 것을 모른다. 어떻게 알 수 있을까?

한번 고민해보자.

?

?

?

흐음..

만약, 필터링 되고 있는 문자열이 아무것도 없다면 컬럼명은 아래와 같이 진행이 가능하다. 

인프런 강의 : 화이트해커가 되기 위한 8가지 웹 해킹 기술 참고(www.inflearn.com/course/%ED%99%94%EC%9D%B4%ED%8A%B8%ED%95%B4%EC%BB%A4%EA%B0%80-%EB%90%98%EA%B8%B0%EC%9C%84%ED%95%9C-8%EA%B0%80%EC%A7%80#)
화이트화이트해커가 되기 위한 8가지 웹 해킹 기술해커가 되기 위한 8가지 웹 해킹 기술
블라인드 인젝션

Step1. 블라인드 인젝션에 사용할 SQL 쿼리의 적정 컬럼 개수 알아내기 (2가지 방법)

1-1. select를 이용하여 알아내기

1' union select 1#  (컬림 개수가 1개인지 알아보는 것)

입력 시, the used SELECT statements have a different number of columns 와 비슷한 문구가 출력되면, 컬럼 개수가 1개는 아니라는 것이다.

1' union select 1, 1#   (컬림 개수가 2개인지 알아보는 것)

만약 데이터가 출력되면 컬럼 개수가 2개인 것이다. 

 

1-2. order by를 이용하여 알아내기

1' order by 10# (컬럼 개수가 최소 10개는 넘는지 알아보는 것)

ORDER BY는 컬림의 개수보다 큰 인자를 받으면 에러가 난다. 즉, 컬럼 개수의 대/소 구문이 가능하다. 

만약, 10# 입력 시 에러가 난다면, 좀 더 숫자를 작게 적어보자.

1' order by 2# (컬럼 개수가 최소 2개는 넘는지 알아보는 것)

만약 데이터가 출력된다면 컬럼 개수가 2개라는 것이다.

 

Step 2. 데이터베이스명 확인하기

컬럼 개수를 알아냈으니 적절한 쿼리를 통해 DB 명을 알아내보자.

1' union select schema_name,1 from information_schema.schemata #

(여기서 1의 의미가 혼란스럽다. 뭘까..?)

**참고로 select schema_name from information_schema.schemata; 는 show databases; 와 같은 출력값을 가져온다.

DB명 출력 값 예시

 

Step 3. 특정 DB의 테이블명 알아내기

1' union select table_schema, table_name from information_schema.tables where table_schema = 'dvwa' #

**참고로 select table_schema, table_name from information_schema.tables where table_schema = 'DB명'은 show tables; 와 같은 출력값을 가져온다.

테이블명 출력 값 예시

 

Step 4. 특정 테이블의 컬럼명 알아내기

1' union select table_name, column_name from information_schema.columns where table_schema = 'dvwa' and table_name = 'users' #

** 참고로 SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = 'DB명' and table_name = '테이블명' 으로 특정 테이블의 컬럼명을 알 수 있다.

출력 값 예시


Step 5. 특정 테이블 특정 컬럼의 내용 출력하기

1' union select user, password from users#

 

하지만 위에서 사용되는 select와 union 문자열은 필터링 되고 있어서 사용할 수 없다..

흠. 일단 password와 pw 둘다 넣어보자. 아무래도 db 관리자도 사람이다 보니 비밀번호 컬럼을 password나 pw로 해놓았을 것 같긴하다. (뜬금없이 비밀번호 컬럼을 id 로 하지는 않을 것이 아닌가?)

 

4번째 시도

admin' AND length(password) > 10 --

No hack라는 결과를 return 한다.

몇 가지 더 테스트 해보니. 필터링 키워드에는 없지만 password 문자열도 필터링 되어지고 있다.

(참고로, 패스워드에 asd를 입력해도 필터링 되는 것 같다.)

pw로 진행해보자.

 

5번째 시도

admin' AND length(pw) > 10 --

False가 출력된다. 흐음. 10보다 큰 것인가? 혹시 구문에 오류는 없을까? 해서 아래와 같이 6번째 시도를 해봤다.

 

6번째 시도

admin' AND length(pw) > 1 --

1자리 수 이상인지 물어봤는데도 False를 리턴한다. 말도 안된다!!

구문에 오류가 있는 것이다. length의 경우 mysql에서 문자열의 길이를 리턴하는 것인데, mysql이 아닌 다른 DB의 문자열 리턴 함수를 사용해보자.

 

7번째 시도

admin' AND len(pw) > 1 --

mssql에서 사용하는 len를 사용해보니 올바른 출력 값이 나온다. 해당 문제는 mysql이 아닌 mssql을 사용하고 있던 것이었다!!

여하튼 숫자를 키워가며 테스트 본 결과, 패스워드는 10자리인 것으로 확인된다.

admin' AND len(pw) > 10 --

이제 패스워드 맨 앞자리 부터 앞파벳을 1개씩 대입해보면서 10자리의 패스워드를 찾아보자.

 

 

8번째 시도

패스워드의 맨 앞자리 문자열이 a 인지 확인하는 구문을 전달해보자.

sql에서 문자열을 가져오는 함수는 left, right, substring(substr) 등이 있다. 하나씩 시도해보자. 

** 참고로 substring은 mssql에서 사용하고 substr은 mysql에서 사용한다.

admin' AND left(pw, 1) == a

admin' AND left(pw, 1) == a

그 중 left, right를 사용해보았으나 2개의 문자 모두 필터링 되어있다.

 

admin' AND substring(pw, 1, 1) == a    (pw 문자열의 1번째부터 1개의 문자열을 가져와라)

오? substring은 먹힌다.

이렇게 하나씩 풀어가다보면 N1c3Bilnl) 문자열이 패스워드로 되는 것을 알 수 있다.

단, 이렇게 10자리의 문자열을 다 대입해보는 것은 너무 비효율적이니, request 모듈을 사용하여 파이썬으로 반복하는 구문을 만들어 보는 것을 추천한다. (2021/01/26 - [Python] - Request 모듈 사용법 참고)

위에서 언급한 참일때의 리턴 값과 거짓일때의 리턴 값을 활용하면, 쉽게 구현이 가능할 것으로 보인다. 

 

참고로 필터링 부분에 char, ascii 등이 언급되어 있는 것으로 보아 문자열에 대해서도 고민이 필요해보인다. 언급한 2개이외에 대표적으로 unicode도 있으니 참고 바란다.

 

 

 

Comments