- La base de datos de checksums de Go y el proxy público de módulos pueden aceptar incluso repositorios que no contienen código Go, lo que crea una vía para cargar datos arbitrarios de repositorios Git en la infraestructura de Go y volver a descargarlos
- Cuando una solicitud a
sum.golang.org/lookup/$module@$versionencuentra una versión de módulo que aún no está registrada, la obtiene del servidor de origen; en este proceso, el zip de ese repositorio también queda disponible enproxy.golang.org - El repositorio Ruby de Homebrew y un fork de un repositorio Rust estaban en la base de datos de checksums, y los experimentos lograron registrar como pseudo-version tanto un módulo Go nuevo como un repositorio sin archivos Go
- Los zips de módulos tienen un límite máximo de 500 MiB, tanto comprimidos como sin comprimir, pero es un tamaño suficientemente grande para evadir restricciones de descarga en máquinas de desarrolladores y CI/CD, almacenar payloads e implementar C2
- De unas 1.59 millones de rutas únicas en
sum.golang.org, unas 1.51 millones eran rutas de GitHub, cerca del 95%, lo que expone tanto la dependencia del ecosistema Go respecto de GitHub como la posibilidad de abusar del proxy público
Repositorios no-Go encontrados en la base de datos de checksums de Go
- Mientras se examinaba la base de datos de checksums de Go,
github.com/homebrew/homebrew-coreaparecía con mucha frecuencia en la tablamodulesgithub.com/homebrew/homebrew-core: 39,438github.com/Homebrew/homebrew-core: 30,896github.com/concourse/concourse: 25,372github.com/openshift/release: 24,065github.com/cilium/cilium: 22,138
- El repositorio de Homebrew es conocido por usar Ruby, y no se encontraron
go.modni archivos fuente Go ni en el repositorio ni en los archivos clonados - La diferencia entre mayúsculas y minúsculas se explica por la regla de case encoding de la documentación de Go
- Las mayúsculas se codifican con
!y la minúscula correspondiente, lo que permite almacenar juntosexample.com/Myexample.com/mincluso en sistemas de archivos que no distinguen mayúsculas de minúsculas
- Las mayúsculas se codifican con
github.com/Edu4rdSHL/rust-headless-chrometambién es un fork de un repositorio Rust no relacionado con Go, pero aparece en la base de datos de checksums
Cómo /lookup obtiene repositorios
- Según la Go Modules Reference de la documentación de módulos de Go, cuando el comando Go consulta la base de datos de checksums, primero obtiene los datos del registro desde el endpoint
/lookup - Si la versión del módulo aún no está registrada en el log, la base de datos de checksums intenta obtener ese módulo desde el servidor de origen antes de responder
- El formato del endpoint es
$base/lookup/$module@$version- Devuelve el número de registro en el log para
$versionde$module, las líneas dego.sumy la descripción firmada del árbol
- Devuelve el número de registro en el log para
- Al consultar una pseudo-version de
github.com/homebrew/homebrew-core, se devuelven un registro de checksum y el hash dego.mod - Si el repositorio no tiene tags de versión, se usan las reglas de pseudo-version de Go
Experimento de registro de un módulo Go nuevo
- Después de crear un nuevo módulo Go,
github.com/gdbinit/fluxmatter, se verificó su registro mediante una solicitudlookup - La consulta
@latestdevolvió un error indicando que no era una versión canónicabad request: version "latest" is not canonical
- La consulta
@v0.0.0devolvió un error indicando que no conocía esa revisionnot found: ... invalid version: unknown revision v0.0.0
- Sin embargo, al volver a sincronizar y consultar la base de datos de checksums, el módulo ya estaba registrado
github.com/gdbinit/fluxmatter|v0.0.0-20240524163826-a7e64ffd69f2|2024-05-24T16:40:51.203837Z
proxy.golang.org/github.com/gdbinit/fluxmatter/@latestdevolvió la pseudo-version e información del origen en GitHub, y también fue posible descargar y verificar la compresión del archivo zip de esa versión- Para la siembra inicial no hace falta especificar una versión exacta: basta con una consulta lookup que contenga la ruta del módulo y un valor que parezca una versión
También se cargan repositorios sin código Go en el proxy público
- El mismo experimento funcionó con el repositorio
github.com/gdbinit/readmem, que no contiene nada de código Go - La solicitud
lookupdevolvió un error indicando que no conocía la revisionv0.0.0, pero el repositorio quedó registrado en la base de datos de checksums como pseudo-versiongithub.com/gdbinit/readmem|v0.0.0-20131006075740-407cb0a56933|2024-05-24T16:45:35.88456Z
- El
@latestdeproxy.golang.orgdevolvió la pseudo-version de ese repositorio y la información del origen Git - El zip descargado contenía
Entitlements.plist,README, archivos de proyecto de Xcode,main.c, etc., no archivos Go - Este experimento usó un repositorio de GitHub, pero podría ser posible en otros sitios de hosting si el VCS funciona
Dependencia de GitHub y límites de tamaño
- La cantidad de rutas únicas en la base de datos de checksums era de 1,591,375, y de ellas 1,515,957 eran rutas
github.com% - Aproximadamente el 95% de las rutas únicas están alojadas en GitHub {p:95}
- Esta cifra es una estadística cruda que no elimina forks ni objetivos que en realidad no sean código Go
- A los zips de módulos Go se les aplican restricciones de ruta y tamaño de archivos
- El archivo zip del módulo puede tener como máximo 500 MiB
- El tamaño total sin comprimir de los archivos también puede ser de máximo 500 MiB
- El archivo
go.modpuede tener como máximo 16 MiB - El archivo
LICENSEtambién puede tener como máximo 16 MiB
- Estas restricciones son un mecanismo para mitigar ataques de denegación de servicio contra usuarios, proxies y otras partes del ecosistema de módulos
- 500 MiB es un tamaño suficientemente grande si se considera su potencial de abuso
Posibles escenarios de abuso
- El proxy público de Go puede usarse para evadir restricciones de descarga por destino en máquinas de desarrolladores o servidores CI/CD
- Se asume una situación sin un
GOPROXYprivado - El malware puede subir un payload a un repositorio y descargarlo desde el proxy cuando lo necesite
- Incluso si desaparece la fuente original, en la entrada de la base de datos de checksums podría quedar solo un rastro pequeño
- Se asume una situación sin un
- Un DoS contra
proxy.golang.orgpodría ser difícil de ejecutar- Se puede pedir al proxy que descargue repositorios Git arbitrarios
- Un posible ataque consistiría en recolectar muchas URL de GitHub y enviar muchas solicitudes a la API
lookup - No se conoce la implementación del servidor, pero podría haber límites de paralelismo, como una work queue
- También es posible que actúen las protecciones de ancho de banda del lado de GitHub
- También existe la posibilidad de un DoS contra el almacenamiento, pero es solo una conjetura
- Es fácil construir C2(command and control) encima de
proxy.golang.org- Con una consulta
@latestse puede encontrar la versión más reciente de un módulo específico - El payload puede ser un archivo simple, o puede ocultarse dentro de
go.modo de archivos fuente Go - Para evitar usar un solo repositorio, se puede usar un module DGA
- Con una consulta
Flujo de descarga de C2
- Para que un implant reciba comandos, basta con realizar estos pasos
- Solicitar
https://proxy.golang.org/module_path/@latest - Extraer la pseudo-version o version del resultado JSON
- Descargar el zip con una solicitud a
https://proxy.golang.org/module_path/@v/version.zip - Descomprimir el contenido del zip y parsear los comandos
- Solicitar
- Este flujo es tan simple que puede implementarse incluso en menos de 300 líneas de código Go
Conclusión y preguntas abiertas
- La base de datos de checksums y el proxy de Go pueden, siguiendo procedimientos documentados, obtener desde el servidor de origen y almacenar módulos que aún no estén registrados
- El estado actual no parece ser un problema grave de la infraestructura de Go, pero puede abusarse de él con facilidad y hay margen de mejora
- Puede haber una razón documentada o privada por la que se permite que repositorios que no son código Go se suban al proxy y a la base de datos de checksums
- Para verificar si alguien ya está abusando de esto, habría que investigar cerca de 1.6 millones de repositorios únicos y alrededor de 22 millones de entradas según la base de datos local más reciente
- Por qué algunos proyectos non-Go válidos están en la base de datos sigue siendo una pregunta abierta
1 comentarios
Opiniones en Hacker News
Cualquier servicio en línea donde los usuarios suben material y ese material aparece públicamente termina usándose para comando y control, infracción de derechos de autor y hosting de CSAM.
Esto ocurre especialmente con servicios que, además de alojar archivos, tienen usos importantes y por eso son difíciles de bloquear; ya pasó con Twitter[1], Telegram[2] e infraestructura de claves PGP[3], por no hablar de objetivos obvios como GitHub.
[1] https://pentestlab.blog/2017/09/26/command-and-control-twitt...
[2] https://www.blazeinfosec.com/post/leveraging-telegram-as-a-c...
[3] https://torrentfreak.com/openpgp-keyservers-now-store-irremo...
En el caso de Gmail, distribuían credenciales para que iniciaran sesión y luego leyeran, vía IMAP, los archivos adjuntos subidos.
Fui SAD-SRE (Spam, Abuse, Delivery) en Google.
También se puede meter un archivo codificado en Base64 dentro de una cadena de código Python.
Soy Googler y esta es mi opinión personal; no conozco bien esta área.
Espero que el equipo de Go haya colaborado con los equipos de GCP y Drive, porque el hosting de archivos maliciosos es un problema que Google maneja constantemente.
No es muy distinto de otros endpoints donde Google ya permite que la gente suba datos arbitrarios.
Google es muy bueno haciendo que equipos centrales administren infraestructura y la compartan en toda la empresa. Salvo cuando se trata de apps de mensajería; y, puramente como conjetura, el equipo de Go probablemente esté usando un almacenamiento interno de blobs, y también debe de haber un equipo interno de infraestructura que automatiza la respuesta ante abusos y el escaneo de archivos.
En PyPI también hay bastantes proyectos que no son Python.
Como puede que los usuarios de Python no puedan compilar el código de una biblioteca, hace falta la capacidad de distribuir wheels, que son binarios compilados.
Ese tipo de código muchas veces está escrito en C, pero también puede ser Golang[1], y aunque no pude encontrar un ejemplo, creo haber visto que se usaba no para bibliotecas sino también para distribuir aplicaciones.
Es bastante genial escribir una app en C, subirla a PyPI y decirles a los usuarios que la instalen con
pip install.[1] https://github.com/popatam/gopy_build_wheel_example
Sería como si en Linux
lsestuviera escrito en Python; creo que es mejor no jugar a ese juego.pipexigiera estar dentro de un entorno como venv/virtualenv/pipenv/pyenv para descargar paquetes.pip install cmake, y los binarios propietarios son posibles comopip install nvidia-cudnn-cu12.Es una de las razones por las que pude abandonar por completo Homebrew.
Quizá sea una idea ingenua, pero no entiendo en qué se diferencia esto de subir archivos a un repositorio de GitHub.
¿La única diferencia es que en GitHub hay que crear una cuenta? En GitHub también se pueden almacenar datos arbitrarios, y tampoco hay un límite de 500 MB.
El sistema de módulos de CUE por fin está saliendo, y aunque MVS es parecido al de Go, está construido sobre infraestructura OCI.
Si te interesan los sistemas de gestión de dependencias, estos enlaces pueden servirte.
proposal: https://github.com/cue-lang/proposal/tree/main/designs/modul...
custom registry: https://cuelang.org/docs/tutorial/working-with-a-custom-modu...
road map: https://github.com/orgs/cue-lang/projects/10/views/8
Desde 0.9.0-alpha-5, los módulos vienen activados por defecto: https://github.com/cue-lang/cue/releases/tag/v0.9.0-alpha.5
En Go Sum, el proyecto Trillian respalda el log de transparencia: https://github.com/google/trillian
CUE planea apoyarse en opciones de OCI como las attestations.
Estuve jugando con la idea de subirme al proxy de golang y a sumdb —es decir, abusar de ellos— para crear un registro de transparencia gratuito de checksums de URLs arbitrarias
https://getsum.pub/
Si lo único que quieres es un registro público de transparencia, la instancia pública de rekor del proyecto sigstore es mucho más adecuada
https://www.sigstore.dev/
https://docs.sigstore.dev/logging/overview/
Quizá sea yo el tonto, pero no entiendo exactamente cuál es el problema aquí
Que el proxy cachee repositorios que no son de Go puede ser un poco desperdicio, pero aunque no hiciera eso, ¿no podrías hacer que cachee repositorios de Go y de todos modos lograr que almacene datos arbitrarios?
Si no me estoy perdiendo de algo, suena como algo totalmente sin importancia
Lo novedoso aquí parece ser apenas que un proxy público sin seguridad acepta cosas para proxyear y las sirve públicamente de forma insegura
El artículo dice que algunas redes monitoreadas podrían confiar más en una URL del proxy de golang que en una URL web arbitraria, por lo que podría usarse para evadir filtros de reputación y cosas así, pero ya hay varias formas de hacerlo y esta no parece especialmente notable
Saliéndome del tema, al ver el dominio me dio mala espina el juego de palabras, así que revisé put.as, y era más o menos lo que esperaba
Es un issue ya conocido: https://github.com/golang/go/issues/31866
.mody.goen la raíz, ¿no?Él y Aaron crearon Athens y, hasta donde sé, Marwan escribió la primera implementación del protocolo de descarga de Go en la que se basó Athens
Lo interesante de este issue es que Athens ya usa el comando
go mod download -json, mencionado como verificación previa de módulosEn general, si el repositorio pasa como un módulo que los comandos de módulos de Go entienden, Athens lo sirve
Dicho en términos más prohibidos, tienes que poder crear versiones de módulo, pseudoversiones y
+incompatible, y ese módulo y sus dependencias tienen que generar checksums válidosLos checksums de módulos actualmente tienen que ver con incluir el
.mody todos los archivos, así como cada dependencia de forma recursivaAsí que, como dice el autor, con solo un programa básico en Go puedes tener por diseño mucho espacio para archivos arbitrarios
El W3C sentó las bases para que todo en la web pudiera cachearse agresivamente, así que es raro que haya tan pocos cachés proxy de propósito general
¿Será que los publicadores están enviando respuestas con
Cache-Control: max-agecorto oVary: Cookieaunque no lo necesiten?¿Será que los ISP están pagando demasiado por tránsito en lugar de peering?
Por ejemplo, en sitios que no usan HTTPS, un proxy del ISP podría insertar anuncios
Las descargas de software suelen tener firmas y checksums, pero el contenido arbitrario rara vez tiene algo así