¿Level 6? ¿Y qué ha pasado con el level 5? El nivel 5 está cocinándose en el server de @marcan42 (prepararlo sobre un server con arquitectura Big Endian requiere su tiempo ;-). Así que vamos a por el level 6. No pudimos superarlo en la competición. Tras la Euskal, con ayuda de Timosoft, supimos por dónde tirar. El título del reto es «A null is a null» y reza así: ‘Hemos recibido una imagen forense de una tarjeta SD, pero no encontramos nada interesante…’. La imagen es un fichero llamado image:
$ file image image: Linux rev 1.0 ext4 filesystem data (extents) (huge files) |
Intentamos montarlo en modo lectura:
$ sudo losetup -r -o 0 /dev/loop0 image $ sudo mount -o ro,noexec,noload /dev/loop0 /tmp/s $ ls -al /tmp/s -rw-r--r-- 1 root root 179201 jul 24 18:55 data.bin drwx------ 2 root root 12288 jul 24 18:55 lost+found |
El directorio lost+found está vacío. El fichero data.bin está compuesto de 0’s.
$ file /tmp/s/data.bin /tmp/s/data.bin: data $ strings /tmp/s/data.bin $ |
Pues qué bien… aquí nos quedamos clavados. @acuartango le dedicó unas cuantas horas a salir del atolladero, pero no conseguimos ver la solución.
La cuestión es que el fichero image es un fichero disperso (sparse). Algunos bloques están ocupados – con 0’s – y otros no. Podemos verlo con este comando:
$ debugfs -R "stat data.bin" image Inode: 12 Type: regular Mode: 0644 Flags: 0x80000 Generation: 2845516278 Version: 0x00000001 User: 0 Group: 0 Size: 179201 File ACL: 0 Directory ACL: 0 Links: 1 Blockcount: 166 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x53d13a76 -- Thu Jul 24 12:55:18 2014 atime: 0x53d13a76 -- Thu Jul 24 12:55:18 2014 mtime: 0x53d13a76 -- Thu Jul 24 12:55:18 2014 EXTENTS: (ETB0):1594, (1-3):1082-1084, (6):1085, (10-11):1086-1087, (14-15):1088-1089, (18-19):2019-2020, (21):2022, (25-26):2026-2027, (29):2030, (34-35):1987-1988, (39):1992, (41):1994, (44-46):1997-1999, (49):2002, (53-55):2006-2008, (57):2010, (62):2015, (66-67):1923-1924, (70-71):1927-1928, (73-75):1930-1932, (77):1934, (81-83):1938-1940, (85-87):1942-1944, (90-91):1947-1948, (94-95):1951-1952, (98-99):1955-1956, (102-103):1959-1960, (105):1962, (108-110):1965-1967, (113-115):1970-1972, (117):1974, (121-122):1978-1979, (124):1981, (130-131):1795-1796, (134-135):1799-1800, (137):1802, (142):1807, (145-148):1810-1813, (151):1816, (153):1818, (155):1820, (157):1822, (161):1826, (165):1830, (167):1832, (169-171):1834-1836, (174-175):1839-1840 |
A null is a null… Si no hay bloque ocupado, tendremos un 0. Si está ocupado, tendremos un 1. Así,
0 --> 0 1-3 --> 111 4 --> 0 5 --> 0 6 --> 1 |
Si agrupamos esa ristra de bits (0’s y 1’s) de 8 en 8, y los interpretamos como caracteres ASCII, tendremos la solución 😉
Un script quick&dirty en Python que lo hace por nosotros:
import re import sys # recuerda ejecutar antes debugfs -R "stat data.bin" image > stats.txt fname = './stats.txt' with open(fname) as f: content = f.readlines() res = [] for line in content: if re.search('(', line): res.append(line) cadena = '' last = 0 for i in res[0].split(',')[1:] : m = re.search('(?P<num>(.*))', i) par = m.group('num') n = re.search('(d+)(-(d+))?', par) inicio = int(n.group(1)) if len(n.groups()) < 2 or n.group(3) is None: fin = inicio else: fin = int(n.group(3)) for j in range( inicio, fin+1): if last < fin: for k in range (last, inicio): cadena +='0' cadena +='1' last = fin+1 string_blocks = (cadena[i:i+8] for i in range(0, len(cadena), 8)) string = ''.join(chr(int(char, 2)) for char in string_blocks) print string |