¿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) |
$ 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 |
$ 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
$ |
$ 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 |
$ 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 |
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 |
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