2021/01/05

Selección de los archivos rescatados de un disco de Apple

Resumen, una persona borró un disco con un montón de backups de la forma:

Backup-AAA-MM-DD

Antes le corrí photorec y recuperé más de dos millones de archivos, iba a borrar algunos y con suerte reducir a medio millón, que igual es mucho, me he apiadado de la persona y le voy a meter más cerebro y trabajo.

 

Debido al conflicto entre hacer un proceso generico y lo particular de la información de la persona, lo que voy a dejar acá registrado no sirve como procedimiento, sirve para que te inspire. Además, no sabés cuántas veces tuve que reeescribir todo esto debido a los cambios producidos por cada nuevo descubrimiento.

En el caso en particular de la Apple, hay una cantidad considerable de archivos de importancia forense pero irrelevantes para mi objetivo.

Cuando estás explorando, como no sabés muy bien a dónde vás, el orden no importa mucho, pero hay que tener en cuenta algunas optimizaciones:

Si vamos a buscar archivos duplicados, hay que calcular md5 de cada archivo, entonces es mejor descartar antes de calcular por que el cálculo de md5 para muchos archivos consume mucho tiempo. 

 

Acciones

 

Hay algunas acciones que hay que repetir tras ejecutar otra, por ejemplo la lista de archivos hay que rehacerla tras renombrar las carpetas con la fecha tentativa.

 

Obtener lista de archivos

 

find . -type f > 00_archivos.txt

 

Obtener e identificar extensiones


cat 00_archivos.txt | rev | \
  cut -d "." -f 1 | rev | sort | \
  uniq -c | sort -nr > extensions_full.txt 

Puede haber basura, en mi caso todos los archivos que no tienen extensión, se pude filtrar o más fácil editar extensions_full.txt

Armé tres blacklists de extensiones:

[system]

plist$
DS_Store$

[photorec]

[0-9]\+.jpg$

[local]

.*txt$
.java$
.h$
.c$
etc...


Podría hacer blacklists más precisas, pero no vale la pena, mil archivos más o menos cuando estás con medio millón no lo amerita, al menos al comienzo. 

 

Descartar extensiones

 

Para el análisis trabajamos sobre 00_archivos con las blacklists:


wc -l 00_archivos.txt
2327955

grep -v -f local_extension_blacklist.txt \
   -f photorec_extension_blacklist.txt \
   -f system_extension_blacklist.txt 00_archivos.txt  | wc -l

890914

 

Archivos y extensiones eliminables


plist

Sirven para guardar información de apliciones, son 400 mil, chau.

 

.DS_Store


Contiene metadata de las carpeta como iconitos y posiciones, 30 mil, chau

 

txt

 

La persona no usa archivo de texto plano y además hay un número desproporcionado, 700 mil. Miré unos veinte al azar y parecen fragmentos xml, chau.

Si la persona hubiera usado archivos txt, debería haber creado alguna regla, ya sea con grep o con yara [1][2] para selecionar.

 

t*.jpg 


Los archivos con nombre t* son thumbnails que photorec extrajo de otros archivos, lo cual está ok para el propósito original de la herramienta, otros 40 mil menos


Analizar la cantidad


Este fué el análisis que me hizo comprender que en realidad no habían carpetas originales, sólo agrupamientos de 500 archivos.


for DIR in *; do
  echo -n "$DIR : "
  ls -1 "$DIR" \
  | grep -v -f photorec_extension_blacklist.txt \
  |  wc -l
done

 

Detectar y eliminar repeticiones

 

Obtener los hashes de los archivos cuyas extensiones no están en las blacklists.

grep -v -f local_extension_blacklist.txt \
   -f photorec_extension_blacklist.txt \
   -f system_extension_blacklist.txt 00_archivos.txt \
  > 01_archivos.txt

cat 01_archivos.txt | while read FILE; do
   md5sum "$FILE";
done > 01_hashes.txt

900 mil hashes, cinco horas...

 

Lo que te queda es:

hash..hash  ./recup_dir.xxx/fxxxxxxx.ext

Esto lo ordena:

sort 01_hashes.txt > 01_hashes.sort.txt

Esto extrae los hashes, elimina y cuenta los repetidos y ordena las frecuencias obtenidas:

cut -b -33 01_hashes.sort.txt | uniq -c | sort -nr \
> 02_only.unique.hashes.txt

Si contamos la líneas, sabemos cuantos archivos quedarán al final:

wc -l 02_only.unique.hashes.txt
243172

Ya casi estamos, salvo que no sé si me alcanza el lugar, veamos cuál es la situación:

  • El disco a rescatar tiene casi 900 GB libres
  • El disco de rescate tiene 120 GB libres
  • El rescate mide 730 GB.

 

Escenarios


Hay varios caminos a tomar:

 

Proyección ingenua

 

Tenía más de dos millones de archivos, queda un décimo de archivos, el rescate en bruto mide 730 GB, con unos 80 GB me arreglo para el rescate neto.

Mi instinto me dice que los archivos más grandes no estan tan repetidos.

 

El camino sin retorno

 

Hago el rescate neto sobre el disco a rescatar.

Lo bueno es que ya queda el disco tal como lo voy a devolver.

Lo malo es que si quisiera repetir el proceso no puedo pues ya pisé una parte.

 

El camino del cobarde con plata

 

Como tengo otro disco más, me puedo dar el lujo de copiar a otro lado.

 

La apuesta ingeniosa

 

Si en lugar de copiar, elimino los repetidos, no hay problema de espacio. Pero si me equivoco o quisiera recuperar algo perdido, tendría que repetir el proceso desde cero.

 

La solución ingeniosa


Si en lugar de borrar o copiar, simplemente muevo las cosas, no hay problemas de espacio, no toco el disco a rescatar, no pierdo los duplicados, que de todos modos no los necesito.


Lo cual me lleva inexorablemente a la respuesta correcta: borrar los repetidos.

La pregunta ahora es por qué hice todos estos rodeos. Muy sencillo. Por un lado no está mal pensar un poco más allá de lo evidente. Y fundamentalmente, por que me quedé enviciado con la idea de que los repetidos me iban a servir para identificar las carpetas, cosa ya descartada, así que a borrar.

¿Cuál sería la regla para borrar?

  • Leer cada linea de 01_hashes_sort, que es (hash,ruta)
    • Si es la primera de una serie de repeticiones, saltear
    • Si no es la primera de una serie, buscar y eliminar la ruta.

Más cerca de la implementación:

  • Leer la primera línea
  • Tomar nota del hash
  • Leer cada línea
    • Si el nuevo hash coincide, eliminar ruta 
    • Si no, tomar nota del nuevo hash

Más cerca de la implemetanción en awk:

  • Decidir que el hash tiene un valor arbitrario imposible
  • Leer cada línea
    • Si el nuevo hash coincide, eliminar ruta
    • Si no, tomar nota del nuevo hash

En awk:

BEGIN { hash="" }

/.*/ {
  if ( $1 == hash ) {
    print "rm " $2 " # " $1;
  } else {
    hash = $1;
    print "# keep " $2 " " $1;
  }
}


Cuando tengo miedo de embarrarla, acostumbro genera la impresión de los comandos en lugar de la ejecución concreta.


awk -f run.awk < 01_hashes.sort.txt > job.sh

Y compruebo que parezca correcto:

grep "# keep " job.sh | wc -l
243172

Y coincide, vamos para adelante.

sh job.sh

No termina nunca, es increiblemente ineficiente, si lo tuviera que hacer otra vez, vería la manera de que cada rm reciba varios archivos a la vez. 


Si estás en el medio del borrado y te asalta la impaciencia, esto te dice el porcentaje de lo realizado, te dejo de ejercicio entenderlo:


echo $(( $(grep -n  -m 1 $(ps ax | grep "rm " | grep -ve grep | grep -o "rm .*" | cut -b 4-) job.sh | cut -d":" -f 1) * 100 / $( grep "rm " job.sh | wc -l  )   ))

 

Si soy muy sagaz quizás ya te diste cuenta del error que cometí, lo noté mientras esperaba que termine el borrado, el primer borrado... ¿ya te diste cuenta?

El análisis de los repetidos fue sobre los archivos que no están en las blacklists de extensiones. Falta borrar esos tambien. Esta vez cada rm recibe 100 archivos

echo -n "rm "
count=0;
grep -f local_extension_blacklist.txt \
     -f photorec_extension_blacklist.txt \
     -f system_extension_blacklist.txt \
     00_archivos.txt \
| while read FILE;  do
   count=$(( $count + 1 ))
   echo -n " $FILE "
   if [ $count -eq 100 ] ; then
     echo
     echo -n "rm "
     count=0;
   fi;
done > job2.sh


wc -l job2.sh
14370

Ese número por 100 más 647764 de los borrados duplicados da 2084764 y más 243172 nos queda 2327936, que se parece bastante al número total de archivos recuperados, 2327955, tiene 19 de diferencia que debe ser el número de archivos de la última línea de job2.sh


 

El final


Y el empujoncito final, agrupar.

Para que la persona pueda hacer algo con todo esto que quedó, generé esta estructura:

  • rescatado
    • imagenes
      • jpg
      • png
      • otras
    • documentos
      • pdf
      • doc
      • docx
      • otros 
    • otros

 

y asi..

El problema es que de algunos tipos de archivos hay hasta más de 10 mil ejemplares y no es buena idea trabajar con carpetas tan grandes, de hecho photorec las hace de 500. Hay que hacer un script tal que tome cada archivo y lo ponga en una carpeta según la jerarquía anterior y además los amontone en carpetas según un límite.


Lo primero es recuperar nuestra frecuencia de extensiones, igual que antes pero con distintos nombre:


find . -type f > 10_archivos.txt

cat 10_archivos.txt | rev | \
  cut -d "." -f 1 | rev | sort | \
  uniq -c | sort -nr > extensions_full.txt

Estos son algunos de mis números:

 

  52000 png
  21000 jpg
  19000 html
  12000 gz
  11000 xml
   8500 pdf
   3000 docx
   1700 gif
   1500 sqlite
   1200 zip
    700 mov
    200 wav
    100 mp3
 

Pensaba particionar pero me cansé, hice un listado para cada tipo (audio, video, comprimidos), con nombre "mover.txt" y luego generé un archivo de patrones:


cat mover.txt| while read EXT; do
   echo ".*\.$EXT$";
done > mover.pattern 

Esto da algo así como:

.*\.mov$
.*\.avi$
.*\.mp4$
.*\.mpg$
.*\.swf$
.*\.webm$

Luego ejecuté:

grep -f mover.pattern 10_archivos.txt | while read FILE; do
  mv "$FILE" rescate/video;
done

Y así para cada grupo.

Elegí algunas extensiones eliminarlas, por ejemplo sqlite. Usé algo parecido a lo anterior, pero

grep -f borrar.pattern 10_archivos.txt | xargs rm

 

Si se le hace inusable a la persona, me dá el disco de regreso y haré el particionamiento.

Para copiar al disco original que tiene hfs plus como filesystem, tuve que pasarme a otra máquina basada en ubuntu para poder instalar hfsprogs, kali no lo tiene a la vista.
 
sudo mount -t hfsplus -o remount,force,rw /media/xxx

Luego, el disco está con root como owner y  hay un usuario 99.99, así que tuve que copiar como root y luego:
 
chown -R 99.99

 

Lo que no fué

 

Esto lo menciono pues me gusta el razonamiento, pero al hacer el análisis de cantidades de archivos por carpeta con lo que pensaba que me ayudaría a descubrir cuáles eran de datos y cuáles del sistema, me encontré con que las carpetas de rescate sólo cumplen la función de no hacer una sola carpeta con millones de archivos, no hay rastro de las estructura, son sólo archivos sueltos.

 

Esperaba que hubieran carpetas que representaran a la misma carpeta pero siendo la misma a travéz del tiempo. De ser así:

  • Hay carpetas del sistema que no nos interesan.
  • Hay carpetas de datos que no nos interesan.

Si en esta instancia eliminara los repetidos, tendría esta situación:

  • carpeta 01 (yo no lo sé pero es la última versión)
    • archivo 01
    • archivo 02
    • archivo 03
  • carpeta 02 
    • archivo 01
    • archivo 02
    • archivo 03

Me resulta más útil poder asignar una fecha a cada carpeta utilizando la fecha más vieja que encuentre adentro pero menor a la fecha de rescate y luego usar las repeticiones para poder relacionar cuales carpetas son la misma.

 

for DIR in  recup_dir.*; do
  date=$( ls "$DIR" --full-time -l \
         | grep -ve 2021-01-01 \
         | head -2 | tail -1 \
         | sed 's/ \+/ /g' | cut -d" " -f 6 )
  if [ $date ] ; then
    echo mv "$DIR" "$DIR.$date"
    mv "$DIR" "$DIR.$date"
  else
    echo "NO DATE FOR $DIR"
  fi
done


Este es el script para revertir:

ls -d recup_dir* | grep "-" | while read DIR;  do
  DST=$(  echo "$DIR" | cut -d "." -f -2 )
  mv $DIR $DST
done


[1] https://seguridad-agile.blogspot.com/2019/11/identificando-versiones-de-programas.html

[2] https://seguridad-agile.blogspot.com/2019/03/jugando-con-los-rayos-1-obteniendo-el.html

 


2021/01/03

Recuperación de archivos en disco de Apple

Una persona conocida tuvo un problemita con un disco con una Apple, ni sabe bien, aparentemente perdió todos los archivos en un reemplazo de discos, no encuentra los discos viejos, conserva un disco de backup al cual borró.

En esta entrada doy una visión quizás menos técnica, es el complemento del contexto teórico que le faltó al rescate de la memoria de una cámara, donde sí están los detalles técnicos.


Existe una baja probabilidad de recuperar algo, veamos qué se puede hacer.

Lo primero es usar una instancia de linux en modo rescate/forense. Esto es, que el sistema no haga ningún tipo de modificación sobre ningún disco sin que se lo pidamos explícitamente. Para este escenario de rescate no es tan necesario pero sí para uno forense, donde hay que conservar intactas las evidencias. Por pura disciplina obremos como si fuera forense.

En el modo forense enfrentamos un adversario, alguien que ha intentado ocultar o eliminar información, en el modo rescate, el adverario es la torpeza del usuario o la mera mala suerte.

Lo ideal sería tener un bloqueador de escritura de hardware como lo que tengo en el trabajo, pero con la cuarentena no tengo acceso. Para windows existe un driver de encase que hace que todo lo que montes esté read-only, pero eso es por la tara de usar windows, en linux alcanza con montar read-only. Es verdad que podés fallar, pero tambien se te puede escapar un sudo rm -rf /


¿a quién no le ha pasado?
¿a quién no le ha pasado?

 

Dado que no tengo ganas de ajustar una instalación normal a "modo forense", voy a usar una máquina cualquiera arrancando con Kali Linux en modo forense, pero sin confiar, le voy a dar otro disco antes y ver que no le haga automount y cuando haga el mount sea read-only.

Kali Linux es una distribución orientada a la seguridad, se puede instalar o usar en modo live, que es lo que haré. No puedo anticipar mucho más pues no sé que voy a encontrar, no sé que particiones usa Apple, no sé nada.

 

Manos a la obra

 

Habiendo iniciado Kali, le conecto un disco cualquiera y verifico que no se haya montado y compruebo montarlo en modo read-only.

 

Es un poco desconcertante que te muestre en el desktop un ícono con las partición disponible para montar. No sé que no entendí de forense, evidentemente es para alguien con mas calle, pues cuando le hice doble click lo montón read-write. Ni me ofreció con el botón derecho el modo de montar.

No importa, es verdad que quien necesita hacer algo forense probablemente dedica buena parte de su tiempo a eso y no se le escapan estos detalles, nosotros los esporádicos, bien atentos.

Conecté entonces el disco a recuperar y uno de capacidad igual o superior. Monté el disco de rescate y tal como mostré en el rescate de la memoria de una cámara, usé photorec, le indiqué origen y destino y a esperar... cuatro horas dijo al comenzar, le llevó....

Seguramente ayudaría que los discos estuvieran conectados en distintas interfaces, pero es lo que tenía, no quiero saber lo que hubiera tardado de tratarse de USB 2.

Asumí que el uso de ambos discos sería similar y por eso puse el pendrive de inicio en USB2 y los discos juntos en USB3. Cuando llevaba 13 horas, para diagnosticar preferí usar ssh en lugar de interactuar directamente con la máquina, pues al ser una notebook, es incómoda por el teclado, el mousepad, la pantalla de muy alta resolución pero poco tamaño y por estar tirada en el piso.

Sólo tuve que ejecutar

sudo service ssh start

para poder entrar remoto.

Antes de investigar, ya que estoy me gustaría ver como marcha photorec, pero no tengo ganas de ir a mirar y desbloquear la sesión:

sudo apt install scrot

export DISPLAY=:0

scrot desktop.png

Ese scrot me captura la pantalla, lo transfiero por sftp y... nada, tiene que estar desbloqueada la sesión, no vale la pena, es menos esfuerzo ir hasta ahí, a menos que...

sudo loginctl unlock-sessions

no funcionó, hay que desactivar el screen-lock, demasiado esfuerzo, lo más correcto es abrir photorec dentro del programa screen para poder des/conectarse desde cualquier sesión, otra vez será.

Ahora falta evitar el sftp.

Para ello, en lugar de conectar con ssh conectamos con ssh -X, que permite abrir allá una aplicación y que su visualización sea aca.

xdg-open desktop.png

Casi, la abre pero no acá, si no allá, por el DISPLAY=:0, hay que decirle a scrot de otra manera.

Además en lugar de abrir con un visor usa un navegador, le lleva mil años, fijate como esta el load average:

 

La carga del sistema
La carga del sistema


Todo el manoseó es entonces:

ssh -X kali@192.168.1.xxx

scrot --display :0 desktop.png

ristretto desktop.png

Ojo que si ya existe desktop.png, sin avisarte salva como desktop_000.png

La mejor manera para la próxima vez:

  • Montar los discos desde la máquina
  • Correr photorec dentro de screen 
  • Hacer detach
  • Conectar con ssh -X
  • Abrir screen y hacer attach

Y cuidado con que no se active nada de Power Saving,  tras estar sin actividad, pues me mató la máquina. Por costumbre al reiniciar eliminé lo que ya había rescatado, probablemente fue un error, pues al arrancar nuevamente photorec, halló un archivo  que evidentemente es el journal y me ofreció continuar la sesión anterior, como que está preparado para sufrir interrupciones, mala suerte, ahí se perdieron dos horas más.

 

Volviendo al análisis del tráfico de los discos, hallé una buena fuente, vamos a probar algunos comandos.

dstat

sudo apt install dstat

Esta herramienta mide de todo, pero hoy sólo nos interesan los discos.

dstat -d 5

 

dstat
dstat

 

No hallé como decirle que use siempre MB para que no cambie la escala.

 

iostat

 

iostat
iostat

Parece indicar lo mismo: Aunque hay más bytes leidos que escritos, no hay una proporción tal que indique que una escritura más lenta podría ser mejor.

Una prueba que quise hacer fue saturar la lectura y la escritura para comparar con la obtenida. Para ello suspendí photorec y mirá lo que pasó con 10 segundos de intervalo:


suspendido
suspendido

 

Estuvo casi un minuto terminando de escribir, eso más bien indica que el cuello de botella es la escritura.

La lectura:

if=/dev/sdc of=/dev/null


saturación lectura
saturación lectura

La escritura:

if=/dev/zero of=borrar.bin

 

saturación escritura
saturación escritura


Para poder seguir de modo responsable el experimento, debería repetir las mediciones para USB2, pero no vale la pena, USB3 es 10 veces más rápido.

 

Recuperación

 

El disco este se usaba como backup, confiando en que el original estaba ok, se borró y se comenzó a usar nuevamente como backup. Luego se cayó en cuenta que el original no estaba ok, desesperación.

Para empeorar la cosa, pasaron varios meses entre las operaciones y la detección del inconveniente.

Lo que espero encontrar son un montón de carpetas borradas de la forma "Backup AAAA-MM-DD" con sus contenidos. Esto implica una alta repetición de archivos.

Para detectarlos voy a usar una técnica como la utilizada cuando lidié con archivos duplicados que es hashear todos los archivos y eliminar los duplicados


Lo que encontré:

5000 carpetas con nombre "recup_dir.NNNN" y con la fecha actual, en una estructura plana, esto es, no hay carpetas anidadas.

2000000 de archivos con nombres de una letra (f, r, t), un número de cerca de diez cifras, de los cuales el 10% tiene un nombre tras "_"

Cuando la letra es:

f: archivo, cuando además tiene un nombre, la fecha correcta, si no la actual.

t: thumbnail hallado dentro de algún archivo, fecha actual.


Al aplicar la técnica de agrupar por md5, se redujo a "sólamente" medio millón de archivos. Al quitar los thumbnails, 30 mil menos.

Abrí algunos archivos al azar y parecen estar ok. 

Los nombres de los archivos no parecen corresponder al nombre original si no a algo tomado de la metadata.


Le dejé a la persona dueña de los datos el trabajito de buscar lo que le interese.