← Alle Beiträge

Eine unzensierte LLM-API für Red-Teaming und Security-Research

20 Aug 2026 · 6 Min. Lesezeit · red-teaming, security

Wenn du schon einmal ein automatisiertes Red-Teaming-Harness gebaut hast, kennst du den Fehlermodus: Dein Angreifermodell weigert sich, den Angriff zu generieren, dein Judge-Modell weigert sich, die Ausgabe zu lesen, die es bewerten soll, und deine Attack-Success-Rate sinkt still und leise. Nichts hat einen Fehler geworfen. Du hast nicht gemessen, dass dein Ziel sicherer wird – du hast gemessen, dass dein Tooling vorsichtiger wird. Eine unzensierte LLM-API existiert, um diese Variable aus der Messung zu entfernen.

Das ist kein Argument dafür, dass Guardrails schlecht sind. Es ist ein Argument darüber, wohin sie gehören. Eine Guardrail in einem Consumer-Chat-Produkt tut ihren Job. Dieselbe Guardrail in einer Evaluationsschleife ist ein unkontrollierter Confounder, der zwischen dir und der Zahl sitzt, die du berechnen willst.

Eine Ablehnung ist ein False Negative – und sieht aus wie Erfolg

Der Grund, warum das so wehtut: Ablehnungen sind keine Fehler. Du bekommst HTTP 200. Du bekommst finish_reason: "stop". Du bekommst wohlgeformtes JSON, das sauber parst. Der String darin lautet „Dabei kann ich nicht helfen“, und deine Pipeline schreibt ihn in eine Datensatzspalte, ein Label-Feld oder einen Report-Abschnitt und macht weiter.

Konkret, an den drei Stellen, wo es am meisten schmerzt:

Angreifermodelle. In einer iterativen Jailbreak-Suche – PAIR, TAP, GCG-artiges Refinement, alles mit einem Angreifer in der Schleife – beendet eine Ablehnung diesen Zweig der Suche. Deine gemeldete Attack-Success-Rate wird zu einer Funktion der Bereitwilligkeit deines Angreifers, nicht der Robustheit deines Ziels. Tausch den Angreifer gegen einen weniger vorsichtigen aus, und die „Verbesserung“, die du für dein Zielmodell ausgeliefert hast, löst sich in Luft auf.

Judge- und Grader-Modelle. LLM-as-Judge-Scoring auf schädlichen Ausgaben setzt voraus, dass der Judge die schädliche Ausgabe tatsächlich liest. Lehnt er ab, bekommst du einen nicht parsbaren Score. Die meisten Harnesses verwerfen diese Zeile. Verworfene Zeilen fehlen nicht zufällig – sie häufen sich genau bei den schweren Ausgaben, die du am dringendsten bewertet brauchst, und dein Aggregat verschiebt sich Richtung „sicher“.

Analysearbeit. Frag ein Mainstream-Modell, was eine dekompilierte Routine tut, und es wird oft ablehnen – wegen der Anwesenheit malware-artiger Tokens, nicht wegen deiner eigentlichen Anfrage. Die Aufgabe war defensiv; der Klassifikator hat auf Vokabular angeschlagen. Dieselbe Geschichte beim Triagieren von Exploit-Traffic in Logs, beim Labeln eines Phishing-Korpus, um einen Detektor zu trainieren, oder beim Aufschreiben eines CTF-Findings.

Weil Ablehnungen lautlos sind, landen Teams am Ende beim Schreiben von Refusal-Detektoren – was für sich genommen ein ungelöstes Problem ist:

python
REFUSAL_MARKERS = ("i can't", "i cannot", "i'm unable", "i won't", "as an ai")

def looks_like_refusal(text: str) -> bool:
    """Substring heuristic. Good enough to alarm on, not good enough to trust:
    it misses polite deflections that never use the phrase, and it fires on any
    completion that quotes a refusal. Both error directions corrupt a dataset."""
    return any(m in text[:200].lower() for m in REFUSAL_MARKERS)

Einen unzuverlässigen Detektor für ein Problem zu bauen, das man an der Quelle löschen könnte, ist der falsche Tausch.

Die API aufrufen

unbleep spricht das OpenAI-Chat-Completions-Schema, jeder Client, den du schon hast, funktioniert also. Base-URL ändern, Key ändern, SDK behalten.

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url="https://unbleep.ai/v1",
    api_key=os.environ["UNBLEEP_API_KEY"],
)

TRIAGE = """You are a malware analyst writing notes for a detection engineer.
Given a decompiled routine, describe what it does, the observable artefacts it
would leave on a host, and where a defender could detect it. Reply as JSON with
keys: behaviour, artefacts, detection_surface, confidence."""


def triage(decompiled: str) -> dict:
    """One sample in, one structured verdict out — no refusal branch to handle."""
    resp = client.chat.completions.create(
        model="unbleep",
        messages=[
            {"role": "system", "content": TRIAGE},
            {"role": "user", "content": decompiled},
        ],
        response_format={"type": "json_object"},
        temperature=0.2,
    )
    return json.loads(resp.choices[0].message.content)

Das Äquivalent über curl, hier beim Labeln eines Phishing-Korpus, um Trainingsdaten für einen Detektor zu erzeugen:

bash
curl https://unbleep.ai/v1/chat/completions \
  -H "Authorization: Bearer $UNBLEEP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "unbleep",
    "messages": [
      {"role": "system", "content": "Label each email for a phishing detector. Answer with one word: phishing or benign."},
      {"role": "user", "content": "Subject: Payroll update required\n\nYour direct deposit is on hold. Confirm at hxxp://payroll-verify.example.com"}
    ],
    "temperature": 0,
    "max_tokens": 4
  }'

temperature: 0 und ein knappes max_tokens zählen in einer Labeling-Schleife mehr als sonst – du willst das Label, nicht einen Absatz, der das Label erklärt.

Eine Stufe wählen

Drei Modelle, alle unzensiert, unterschiedlich in Kontext und Preis:

unbleep und unbleep-high sind Reasoning-Stufen: Sie denken nach, bevor sie antworten, und liefern den Trace in einem Feld reasoning_content neben content zurück. Für ein Judge-Modell ist dieser Trace wirklich nützlich – er sagt dir, warum eine Zeile den Score bekommen hat, den sie bekommen hat, und genau das brauchst du, wenn du eine Abweichung auditierst. Er wird aber auch als Output abgerechnet, schick also "thinking": false bei Jobs, die ihn nicht brauchen. unbleep-mini antwortet direkt und liefert keinen Trace.

Governance, die du kontrollierst

Refusals aus dem Modell zu entfernen heißt nicht, die Aufsicht aus deiner Pipeline zu entfernen. Der optionale Parameter policy führt Governance auf unserer Seite aus, pro Request:

python
resp = client.chat.completions.create(
    model="unbleep",
    messages=[...],
    extra_body={"policy": "research"},  # recorded on the usage row; answers exactly like off
)

off ist die ungefilterte Standard-Baseline. research antwortet exakt wie off – die Stufe wird auf der Usage-Zeile festgehalten, damit du in deinem eigenen Reporting Evaluations-Traffic von Baseline-Traffic trennen kannst. Es findet keine zusätzliche Prüfung statt. strict prüft den Nachrichtentext gegen unsere vom Betreiber gepflegte Service-Blocklist und liefert bei einem Treffer ein 422. Diese Liste ist für alle gleich, die sich dafür entscheiden – es gibt keine Blocklist pro Account zu konfigurieren –, betrachte sie also als Notbremse, nicht als deine Produktions-Content-Policy.

Eines solltest du klären, bevor du eine Pipeline hierauf richtest: Wir speichern, was du sendest und was zurückkommt. Der Request-Body, der Antwortinhalt und die Quell-IP jedes Aufrufs, den wir annehmen und an ein Modell weiterleiten, werden in unsere Datenbank geschrieben und 30 Tage aufbewahrt – für Missbrauchsuntersuchungen, Support und Abrechnungsstreitigkeiten. Bodies werden bei 64 KB abgeschnitten. Die Quell-IP überlebt die 30 Tage: Sie wird zusätzlich in die Usage-Zeile geschrieben, die den Aufruf abrechnet, und diese Zeile ist ein Finanzdatensatz, den wir länger aufbewahren. Das Einzige, was nicht gespeichert wird, ist ein Request, den strict ablehnt, bevor er ein Modell erreicht: Der taucht in deiner Usage-Historie ohne Inhalt auf. Sonst ist nichts ausgenommen, und es gibt keine Einstellung, um das abzuschalten. Wenn dein Korpus so sensibel ist, dass eine 30-Tage-Kopie außerhalb deines Perimeters ein Problem ist – Live-Malware, Kundendaten, ein Engagement unter NDA –, dann ist das eine echte Einschränkung, und die Datenschutzerklärung ist die Seite, die du liest, bevor du anfängst, nicht danach.

Fehler folgen dem OpenAI-Envelope, bestehende Handler funktionieren also: 401 ungültiger Key, 402 kein Guthaben, 422 von der Policy blockiert, 429 Rate-Limit, 5xx wiederholbar. Jede Antwort trägt x-ratelimit-*-Header, ein Batch-Job kann sich also dosieren, ohne zu raten.

Was eine unzensierte LLM-API dir nicht gibt

Ein unzensiertes Modell ist ein schärferes Werkzeug, kein freizügiges. Es beantwortet Fragen, die das Basismodell abzulehnen gelernt hat – auch solche, die du nicht stellen solltest –, und zwar mit derselben Selbstsicherheit, die es auf alles andere anwendet. Das heißt auch: Wo ein abgesichertes Modell aus Unwissen abgelehnt hätte, erfindet dieses womöglich einfach etwas. Halte einen Menschen für das verantwortlich, was herauskommt. Die Richtlinie zur zulässigen Nutzung sagt klar, wofür die API da ist und für welche wenigen Dinge sie nie da ist; der Maßstab sind Autorisierung und Absicht, nicht Vokabular. Rechtmäßige Nutzung liegt in deiner Verantwortung.

Hol dir einen API-Key und hör auf, die Vorsicht deines Toolings zu messen.