Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2021-28378 — Prova di concetto dettagliata e analisi di CVE-2021-28378, una vulnerabilità XSS memorizzata in Gitea che consente l'iniezione arbitraria di codice, l'escalation dei privilegi e l'esecuzione remota di codice tramite git hooks. | Kitploit
Strumenti/GitHubGitHub/pandatix/cve-2021-28378
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebPenetration TestingApprendimento e FormazioneSviluppo Payload
GitHubpandatix/cve-2021-28378

CVE-2021-28378

Prova di concetto dettagliata e analisi di CVE-2021-28378, una vulnerabilità XSS memorizzata in Gitea che consente l'iniezione arbitraria di codice, l'escalation dei privilegi e l'esecuzione remota di codice tramite git hooks.

Vedi Repository
4229 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2021-28378

Dettagli su questa CVE qui.

Questa CVE riguarda la mancanza di escaping delle stringhe lato client per i contenuti recuperati dal server. Consente a un attaccante di iniettare facilmente codice arbitrario creando un commento su un issue o una pull request. È stata introdotta nel commit 7d7ab1eeae43d99fe329878ac9c8db5e45e2dee5, alla fine di gennaio 2020, ma a causa di uno squash non possiamo sapere esattamente chi l'ha introdotta per cercare bug simili.

Prima di tutto, capiamo da dove arriva questa vulnerabilità. Per farlo, vediamo il commit che risolve questa CVE.

Prima:

  • Primo:
    labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${label.name}</div>`;
  • Secondo:
    html: `
<div>
    <p><small>${issue.repository.full_name} on ${createdAt}</small></p>
    <p><span class="${color}">${svg(octicon)}</span> <strong>${issue.title}</strong> #${index}</p>
    <p>${body}</p>
    ${labels}
</div>
`

Possiamo vedere che non c'è alcun escaping html su label.name, issue.repository.full_name, issue.title e body, che sono stringhe manipolabili da un utente, e quindi possono iniettare codice arbitrario. Nota che issue.repository.full_name non può essere realmente manipolato per iniettare codice poiché deve seguire troppe regole ([\w._-]+) che non possiamo bypassare con questa CVE. Questo è esattamente ciò che la PR risolve convertendole in htmlEscape.

Dopo:

  • Primo:
    labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${htmlEscape(label.name)}</div>`;
  • Secondo:
    html: `
<div>
    <p><small>${htmlEscape(issue.repository.full_name)} on ${createdAt}</small></p>
    <p><span class="${color}">${svg(octicon)}</span> <strong>${htmlEscape(issue.title)}</strong> #${index}</p>
    <p>${htmlEscape(body)}</p>
    ${labels}
</div>
`

Comprendere la vulnerabilità

Diamo un'occhiata al file web_src/js/features/contextpopup.js, per capire come attivare la CVE.

...
const {AppSubUrl} = window.config;

export default function initContextPopups() {
  const refIssues = $('.ref-issue');
  if (!refIssues.length) return;

  refIssues.each(function () {
    const [index, _issues, repo, owner] = $(this).attr('href').replace(/[#?].*$/, '').split('/').reverse();
    issuePopup(owner, repo, index, $(this));
  });
}

function issuePopup(owner, repo, index, $element) {
  $.get(`${AppSubUrl}/api/v1/repos/${owner}/${repo}/issues/${index}`, (issue) => {
...
    for (let i = 0; i < issue.labels.length; i++) {
...
      labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${label.name}</div>`;
    }
...

    $element.popup({
      variation: 'wide',
      delay: {
        show: 250
      },
      html: `
<div>
  <p><small>${issue.repository.full_name} on ${createdAt}</small></p>
  <p><span class="${color}">${svg(octicon, 16)}</span> <strong>${issue.title}</strong> #${index}</p>
  <p>${body}</p>
  ${labels}
</div>
`
    });
  });
}

Quello che vediamo è che per ogni nodo con la classe ref-issue, viene aggiunto un popup vulnerabile. Ora, dobbiamo trovare dove un ref-issue viene inviato all'utente: facciamo un grep (e teniamo solo i risultati interessanti, quindi niente file di test o simili)! Il seguente numero di commit corrisponde al tag 1.12.4, la versione predefinita del file docker-compose.yml ufficiale, che è affetto da questa CVE.

cd gitea
git checkout 8a51c48eb6367513e0518bcd412e64f6cbfe5e1a
grep -r ref-issue

modules/markup/html.go:		replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue"))
modules/markup/html.go:		replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue"))
modules/markup/html.go:		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), reftext, "ref-issue"))
modules/markup/html.go:			link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], path, ref.Issue), reftext, "ref-issue")
modules/markup/html.go:			link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue")
modules/markup/sanitizer.go:	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")

Ora sappiamo che non esiste contenuto statico con la classe ref-issue, ma viene generato dinamicamente. L'ultima riga non è interessante poiché è solo una regola di policy del sanitizer. Le altre sono interessanti, vedremo perché.

Analizziamo modules/markup/html.go.

func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) {
	if ctx.metas == nil {
		return
	}
	m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
	if m == nil {
		return
	}
	link := node.Data[m[0]:m[1]]
	id := "#" + node.Data[m[2]:m[3]]

	// extract repo and org name from matched link like
	// http://localhost:3000/gituser/myrepo/issues/1
	linkParts := strings.Split(path.Clean(link), "/")
	matchOrg := linkParts[len(linkParts)-4]
	matchRepo := linkParts[len(linkParts)-3]

	if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] {
		// TODO if m[4]:m[5] is not nil, then link is to a comment,
		// and we should indicate that in the text somehow
		replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue"))

	} else {
		orgRepoID := matchOrg + "/" + matchRepo + id
		replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue"))
	}
}

Dovremo anche capire cosa fa getIssueFullPattern.

func getIssueFullPattern() *regexp.Regexp {
	if issueFullPattern == nil {
		appURL := setting.AppURL
		if len(appURL) > 0 && appURL[len(appURL)-1] != '/' {
			appURL += "/"
		}
		issueFullPattern = regexp.MustCompile(appURL +
			`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
	}
	return issueFullPattern
}

Riassumiamo per capire cosa fa questo codice.

In fullIssuePatternProcessor, dobbiamo prima assicurarci che ctx.metas non sia nil (cosa che assumiamo vera poiché viene costruito da funzioni/metodi più in alto nello stack). Poi controlliamo se i dati del nodo corrispondono a un percorso come http://mygitea.com/gituser/myrepo/issues/1. Se c'è una corrispondenza, lo sostituiamo con un link. Se il link riguarda lo stesso utente e repository, lo sostituiamo con il pattern #<issue_id>, altrimenti con <user>/<repo>#<issue_id>. In entrambi i casi, viene aggiunta una classe ref-issue per generare il popup.

Gli altri risultati del grep fanno più o meno la stessa cosa.

È ora di giocare!

Ora che abbiamo il contesto, che riguarda la sostituzione di un link come http://mygitea.com/gituser/myrepo/issues/1 con un link più corto, possiamo dedurre dove viene fatto questo lavoro: negli issue e nelle PR, dove puoi aggiungere un'etichetta e/o postare un commento.

Per questo, supponiamo di avere un repository con permessi di scrittura.

Etichetta

Vai al tuo repository e crea una nuova etichetta con un nome contenente codice arbitrario (ad esempio <script>alert('label.name')</script>).

Issue

Vai agli issue del tuo repository e crea un nuovo issue con codice arbitrario nel titolo e nel corpo, e aggiungi l'etichetta precedente.

Eseguiamo del codice

Crea un nuovo issue su un qualsiasi repository e aggiungi nel suo corpo un link al tuo issue iniettato come http://mygitea.com/gituser/myrepo/issues/1. Verrà sostituito dal link che abbiamo visto prima, e quando qualcuno passerà il mouse sopra il link, il codice arbitrario verrà eseguito: verrà mostrato un popup che contiene label.name.

Sfruttamento

La CVE-2021-28378 è dimostrata essere in grado di consentire l'iniezione di codice arbitrario. Immaginiamo cosa potrebbe fare un attaccante.

Scarica lo strumento