<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: arturo melgarejo</title>
    <description>The latest articles on DEV Community by arturo melgarejo (@arturo0x90).</description>
    <link>https://dev.to/arturo0x90</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3769054%2F736f8341-1b91-4541-99c5-19df42a24475.png</url>
      <title>DEV Community: arturo melgarejo</title>
      <link>https://dev.to/arturo0x90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arturo0x90"/>
    <language>en</language>
    <item>
      <title>Orquestacion multiagente con Openclaw dockerizada.</title>
      <dc:creator>arturo melgarejo</dc:creator>
      <pubDate>Fri, 17 Apr 2026 19:50:22 +0000</pubDate>
      <link>https://dev.to/arturo0x90/orquestacion-multiagente-con-openclaw-dockerizada-5h1b</link>
      <guid>https://dev.to/arturo0x90/orquestacion-multiagente-con-openclaw-dockerizada-5h1b</guid>
      <description>&lt;h1&gt;
  
  
  Orquestacion multiagente con Openclaw dockerizada.
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/Arturo0x90/OpenClawMultiAgent" rel="noopener noreferrer"&gt;&lt;code&gt;Enlace a GitHub&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;— Un panel de control que gestiona agentes con OpenClaw separados en contenedores Docker por "departamentos", les deja delegarse tareas entre ellos, y mantiene un &lt;strong&gt;contexto compartido&lt;/strong&gt; persistido en base de datos para que no se pierdan en la conversación. En este post enseño como funciona el panel y un poco la logica:&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  La idea
&lt;/h2&gt;

&lt;p&gt;Cuando OpenClaw estaba todavía en pañales y Paperclip ni existía, se me ocurrió una idea que no me dejaba tranquilo: &lt;strong&gt;¿y si los agentes pudieran hablarse entre ellos de forma estandarizada, con un contexto compartido, como si fueran un equipo de verdad?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;De ahí salió este último repo que acabo de subir a GitHub. En resumen, es un software que &lt;strong&gt;orquesta agentes, los separa en contenedores Docker y les da una capa de comunicación + delegación de tareas con contexto común&lt;/strong&gt;. En este post te enseño de lo que es capaz.&lt;/p&gt;




&lt;h2&gt;
  
  
  El Control Room
&lt;/h2&gt;

&lt;p&gt;Asumiendo que ya lo tienes todo configurado y corriendo — mínimo uno o más contenedores de &lt;code&gt;AGENTE_MCP_API_V2&lt;/code&gt; (sí, horrible el nombre de la carpeta, lo asumo) y un servidor — al entrar te recibe esto:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfk07zupidhml1xrxig8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfk07zupidhml1xrxig8.png" alt="Control Room — vista general del panel" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Te dejo una mini leyenda para que te orientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔴 &lt;strong&gt;En rojo&lt;/strong&gt; — el indicador de si el panel se está comunicando con la API. Si pone &lt;strong&gt;CONECTADO&lt;/strong&gt;, todo ok (o minimo la conexion con el contenedor de la api)&lt;/li&gt;
&lt;li&gt;🔵 &lt;strong&gt;En azul&lt;/strong&gt; — un preview en tiempo real de los agentes que están trabajando. Ojo al detalle: si a un agente se le &lt;strong&gt;ilumina la cabeza con una lucecita&lt;/strong&gt;, es que está procesando algo (tiene tareas pendientes).&lt;/li&gt;
&lt;li&gt;🟡 &lt;strong&gt;En amarillo&lt;/strong&gt; — el contador de agentes y de departamentos que tienes activos.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Consejo de uso:&lt;/strong&gt; lo ideal es levantar &lt;strong&gt;varias instancias de &lt;code&gt;AGENTE_MCP_API_V2&lt;/code&gt;&lt;/strong&gt;, una por departamento. Así cada grupo de agentes vive en su propia burbuja y no se lían entre ellos pisándose tareas o contexto. Es justo para lo que está pensada la aplicación.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Live Floor
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pl6bzn5u5o85fgb1nog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pl6bzn5u5o85fgb1nog.png" alt="Live Floor — animación en tiempo real con mesas por departamento" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si pulsas en &lt;strong&gt;Live Floor&lt;/strong&gt;, ves básicamente lo mismo que tienes en la barra de abajo, pero un pelín más &lt;em&gt;fancy&lt;/em&gt;: le monté unas mesas a los agentes para que cada departamento tenga su espacio bien separado. Es más escaparate que funcionalidad, pero queda resultón y se entiende de un vistazo quién curra con quién.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agentes:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqlgbrsdsmihw72cywlo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqlgbrsdsmihw72cywlo.png" alt="Pestaña de Agentes con detalle por departamento y gestión de contactos" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En la pestaña de &lt;strong&gt;Agentes&lt;/strong&gt; tienes una vista más detallada de cada uno, y lo importante: aquí puedes gestionar sus &lt;strong&gt;contactos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Y ojo, porque los contactos son &lt;strong&gt;vitales&lt;/strong&gt;: los agentes los leen automáticamente y, en base a eso, saben con quién pueden hablar y con quién no. Por si acaso, también está capado &lt;strong&gt;a nivel de API&lt;/strong&gt;, así que no es solo una barrera "visual".&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué tanto lío con los contactos?
&lt;/h3&gt;

&lt;p&gt;Para respetar el principio de &lt;strong&gt;modularidad&lt;/strong&gt; del que hablábamos al principio. Imagínate que tienes un developer en un departamento y otro developer en otro: no quieres que se pongan a hablar entre ellos directamente y se monten su propio mundo paralelo.&lt;/p&gt;

&lt;p&gt;Lo que quieres es que, si hay que coordinar algo entre departamentos, se comuniquen los &lt;strong&gt;CEOs&lt;/strong&gt; de cada uno — y que ellos ya bajen la información a su gente.&lt;/p&gt;

&lt;p&gt;Es la misma lógica que en una empresa real: las jerarquías y los canales de comunicación existen por algo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tareas
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwjxqs5naxnv9pfk2olb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwjxqs5naxnv9pfk2olb.png" alt="Pestaña de Tareas con listado en tiempo real y creación manual" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En &lt;strong&gt;Tareas&lt;/strong&gt; puedes ver todo lo que se está cociendo ahora mismo. Si le das a &lt;strong&gt;Cargar historial de tareas completadas&lt;/strong&gt;, también te aparece la lista completa de lo que se ha ejecutado hasta el momento. Es también el sitio desde el que puedes &lt;strong&gt;crear tareas nuevas a mano&lt;/strong&gt; — útil si no quieres lanzarlas desde Telegram, por ejemplo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aviso con la creación:&lt;/strong&gt; por diseño está un poco "cutre" — hay que indicar que el &lt;strong&gt;padre es el mismo que el hijo&lt;/strong&gt;, pero funciona perfectamente. Nota mental para v2 😅.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Ejemplo de delegación
&lt;/h3&gt;

&lt;p&gt;Para que se entienda, un caso sencillo: creamos una tarea nueva para &lt;strong&gt;Alfa&lt;/strong&gt; pidiéndole que &lt;strong&gt;Beta&lt;/strong&gt; nos prepare un informe. Ahí ya tienes un caso de delegación — Alfa no hace el informe, se lo pasa a Beta, y luego nos devuelve los resultados.&lt;/p&gt;

&lt;h3&gt;
  
  
  La parte que más mola: el contexto compartido
&lt;/h3&gt;

&lt;p&gt;Aquí viene una de las cosas que más me gustan del proyecto: puedes ver el &lt;strong&gt;contexto de la tarea de forma totalmente transparente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El software integra un sistema de contexto compartido que vive en sesiones y se persiste en la &lt;strong&gt;base de datos&lt;/strong&gt; (los agentes siempre tienen que incluir un resumen, es obligatorio). Y la gracia es esta:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Todas las tareas hijas de una misma tarea padre, y las que estas generen a su vez, comparten el mismo contexto.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Es decir, el "hilo" de conversación entre agentes &lt;strong&gt;no se rompe&lt;/strong&gt; por mucho que la tarea se ramifique.&lt;/p&gt;

&lt;p&gt;Aquí un ejemplo real del contexto compartido en acción:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxfk4wq0x1okbzmuct2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxfk4wq0x1okbzmuct2q.png" alt="Ejemplo real de contexto compartido entre Alfa y Beta en una tarea delegada" width="657" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se ve clarísimo cómo &lt;strong&gt;Alfa delega a Beta&lt;/strong&gt;, &lt;strong&gt;Beta completa el reporte&lt;/strong&gt;, y luego &lt;strong&gt;Alfa recoge el resultado&lt;/strong&gt; — todo dentro del mismo contexto compartido de la tarea. Esta parte me gustó muchísimo desarrollarla, precisamente por lo visual que queda ver a los agentes "hablando" entre ellos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reportes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqx6hv6hd344nvy6ii7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqx6hv6hd344nvy6ii7r.png" alt="Pestaña de Reportes con el resultado de la tarea padre" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y para cerrar, la pestaña de &lt;strong&gt;Reportes&lt;/strong&gt;. Cuando una tarea &lt;strong&gt;padre&lt;/strong&gt; (la que tú lanzas desde fuera) termina, el agente detecta que era el origen de la cadena y te genera &lt;strong&gt;automáticamente&lt;/strong&gt; un reporte con lo que ha pasado — o con lo que tú le hayas pedido que te reporte, si lo especificaste. En este caso puedes ver el reporte de la tarea del ejemplo anterior.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Y ahora qué?
&lt;/h2&gt;

&lt;p&gt;Esto es una pre pre ante supra beta jajaja, así que sí: tiene &lt;em&gt;rough edges&lt;/em&gt;, cosas por pulir y funcionalidades que me gustaría llevar más lejos en una v2. Pero funciona, y creo que la idea de &lt;strong&gt;separar agentes en contenedores + contexto compartido persistido + delegación con trazabilidad&lt;/strong&gt; tiene recorrido.&lt;/p&gt;

&lt;p&gt;No se si sere capaz con tantas cosas por hacer de continuarla, pero te animo a ti a que le eches un vistazo, unas horas en entenderlo y aprender, y la mejores!&lt;/p&gt;

&lt;p&gt;Si te pica la curiosidad, te animo a echarle un ojo al repo, probarlo y romperlo un rato — cualquier feedback, issue o PR es bienvenido.&lt;/p&gt;

&lt;p&gt;Y si te ha gustado el post, sígueme por aquí o en LinkedIn para ver cómo evoluciona el proyecto. 🙌&lt;/p&gt;

</description>
      <category>agents</category>
      <category>docker</category>
      <category>openclaw</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Baby-Cached WriteUp</title>
      <dc:creator>arturo melgarejo</dc:creator>
      <pubDate>Thu, 12 Feb 2026 15:10:46 +0000</pubDate>
      <link>https://dev.to/arturo0x90/baby-cached-writeup-ck5</link>
      <guid>https://dev.to/arturo0x90/baby-cached-writeup-ck5</guid>
      <description>&lt;p&gt;El challenge consiste en un Side-Server Request Forgery, y alguna cosa mas que hay que resolver antes de poder conseguir la flag.&lt;br&gt;
Es un reto de la categoría Web en HackTheBox, siendo su dificultad Facil , pero originalmente catalogado como Medio. Esta retirado, por lo que no hay problemas en hacer writeups.&lt;/p&gt;

&lt;p&gt;El funcionamiento de la pagina es el siguiente: Le pasas una URL y el te devuelve una screenshot de la misma.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb66t2qys4qvx69s7uhmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb66t2qys4qvx69s7uhmu.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explotación&lt;/strong&gt;&lt;br&gt;
No haría ni falta ver el codigo, ya que analizandolo con Burp y inspeccionando la web nos damos cuenta que no hay apis escondidas, ni nada mas.&lt;/p&gt;

&lt;p&gt;El único input que podemos explotar entonces es la pagina web. No obstante aunque sepamos que es un SSRF, necesitamos ver como explotarlo y que grado de severidad tiene, para eso inspeccionamos el codigo. Observamos que dentro de las rutas disponibles hay una que carga una imagen flag.png. No hay mas preguntas señoria. Ese es nuestro objetivo. Pero solamente se puede acceder si url origen es 127.0.0.1.&lt;/p&gt;

&lt;p&gt;Vemos una llamada a una función &lt;strong&gt;cachear_web&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2h9ef0zn5prz2y9c1g6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2h9ef0zn5prz2y9c1g6.png" alt=" " width="553" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como curiosidad, aunque no nos va a hacer falta para nuestro reto, no nos deja usar FTP o GOPHER en vez de HTTP en la url para poder pivotar a otros servicios. No es relevante. Además vemos que se realiza una comprobación de si el hostname REAL que le hemos pasado es localhost o alguna forma de llegar directamente hacia el mismo.&lt;br&gt;
Esto provoca que el exploit no sea tan sencillo de poner simplemente &lt;a href="http://127.0.0.1/flag" rel="noopener noreferrer"&gt;http://127.0.0.1/flag&lt;/a&gt; por ejemplo.&lt;/p&gt;

&lt;p&gt;Entonces ¿Como lo explotamos?. Aunque no lo he mencionado, usa Selenium, para cargar la web y posteriormente una vez cargada o pasados 10 segundos, lo que antes ocurra, hace una screenshot.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kazx9ethfe2wver3h1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kazx9ethfe2wver3h1z.png" alt=" " width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La clave esta en saber que es selenium. Selenium es un webdriver que tiene librerías disponibles en varios lenguajes de programacion. Esencialmente permite la automatización de paginas web humanizando el proceso, es decir, abre el navegador literalmente en la maquina como si fueras tu mismo. Vamos a bypassear por tanto los filtros con nuestra propia pagina web.&lt;br&gt;
La idea es muy simple: Crear una pagina web que cargue contenidos de una url de la misma maquina (127.0.0.1), de esta forma no le pasamos directamente un 127.0.0.1, si no que le pasamos un html que carga contenidos de esa web deseada.&lt;/p&gt;

&lt;p&gt;En un principio me complique y empece a hacerlo con javascript, cosa que hubiera funcionado y es totalmente legitima… ¿lo es?&lt;br&gt;
No, no lo era xd, por alguna extraña razon (ya que es mas complejo que la segunda solucion), habían deshabilitado javascript. Puede ser por seguridad. No lo vi.&lt;/p&gt;

&lt;p&gt;Vamos a hacerlo mucho mas sencillo. Los pasos son los siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crearemos una web con Flask que cargue una imagen desde 127.0.0.1&lt;/li&gt;
&lt;li&gt;Bypassearemos los firewalls de nuestra casa con un proxy inverso del estilo ngrok o en mi caso el de cloudfare.&lt;/li&gt;
&lt;li&gt;Finalmente, enviaremos a la web vulnerable la url de la nuestra.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Codigo del servidor:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;app.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask, send_file, request, jsonify, render_template
from werkzeug.utils import secure_filename

aplicacion = Flask

app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_reenviada():
 if 'archivo' in request.files:
  archivo = request.files['archivo']
  filename = archivo.filename
  archivo.save(os.path.join('upload', secure_filename(filename)))
  return "ok"
 return "error"

@app.route('/1', methods=['GET'])
def pagina_principal():
 return render_template('prueba.html')

if __name__ == "__main__":
 app.run('0.0.0.0', 80)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;La ruta upload es un extra para usar javascript y enviar el archivo desde el cliente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;prueba.html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;img src="http://localhost/flag"&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comandos bash reverse proxy — Cloudfare y setup servidor&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 app.py
./cloudflared-linux-amd64 -url http://localhost:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La url que devuelva cloudfare, se la pasamos a la web vulnerable con la ruta /1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0auruowqgao4x98pi1fk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0auruowqgao4x98pi1fk.png" alt=" " width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>selenium</category>
      <category>server</category>
      <category>python</category>
    </item>
    <item>
      <title>CandyVault - WriteUp</title>
      <dc:creator>arturo melgarejo</dc:creator>
      <pubDate>Thu, 12 Feb 2026 15:00:42 +0000</pubDate>
      <link>https://dev.to/arturo0x90/candyvault-writeup-fli</link>
      <guid>https://dev.to/arturo0x90/candyvault-writeup-fli</guid>
      <description>&lt;p&gt;Este es el primer WriteUp que escribo en la pagina de dev.to. La razon principal de empezar a crear estos writeups es porque considero que el documentar que es una buena forma de medir y guardar el progreso, asi como contribuir y ayudar en el aprendizaje de otros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducción&lt;/strong&gt;&lt;br&gt;
Este reto pertenece a la categoría very-easy de los challenges Web de HackTheBox. Cuando tengo poco tiempo realizo este tipo de retos, ya que solo sueles tener que romper la lógica, o vulnerar una vez, no tienes que encadenar varios fallos de diseño o vulnerabilidades.&lt;br&gt;
Este reto consiste en explotar una vulnerabilidad de NOSQL injection, mas especificamente, usa MONGODB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fase de reconocimiento y búsqueda de vulnerabilidades&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sin necesidad de replicar la instancia del proyecto en Docker, descargamos el archivo de proyecto y navegamos hasta ./challenge/application.&lt;/p&gt;

&lt;p&gt;Al editar el archivo app.py, reconocemos que el servidor usa como framework Flask. Normalmente se suele usar Django para aplicaciones web con python, pero en este caso usan Flask que es mas para pequeñas webs, aplicaciones en desarrollo que requieren de un servidor web, y en general para pruebas sobre proyectos. Por esa razon se suele obviar cambiar el modo de Debug a Producción. No obstante nuestro servidor usa en run.py:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;debug=False&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ademas, vemos que la aplicacion usa MongoDB. Para conectarse usa una clave que la coge a través de config.py, y el mismo hace una llamada a una variable de entorno que no podríamos leer. Por tanto (parece) que a menos que podamos leer las variables de entorno o un objeto que queramos, no podríamos acceder a la BD.&lt;/p&gt;

&lt;p&gt;Sin mucho mas analizar el archivo, que es muy sencillo y no tiene mas módulos que app.py, nos damos cuenta que cuando nos autentiquemos nos devolverá la flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user = users_collection.find_one({"email": email, "password": password})

if user:
    return render_template("candy.html", flag=open("flag.txt").read())

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fijémonos que a demas de decirnos que es lo que tenemos que hacer, ya nos da una gran pista sobre que rumbo tomar para explotar la aplicación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if content_type == "application/x-www-form-urlencoded":
    email = request.form.get("email")
    password = request.form.get("password")

elif content_type == "application/json":
    data = request.get_json()
    email = data.get("email")
    password = data.get("password")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;hmm… Curioso ¿Porqué una aplicacion querria aceptar tanto json como POST normal? Obviamente porque quieren que lo veamos y vayamos directos al grano. Aqui ocurre una especie de error parecido al de deserialización con PHP para usar objetos y conseguir explotación remota. Solo que en nuestro caso, lo usaremos para hacer una injección a mongodb.&lt;br&gt;
Sabemos que una petición “normal” en json seria&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "email": "ejemplo@medium.us",
  "password": "2024miperro"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y tambien sabemos que la aplicacion usa data.get(), lo cual nos permite poder crear un objeto diferente a una string, ya que en python las variables se les define el tipo/objeto en tiempo real&lt;/p&gt;

&lt;p&gt;Además (Estamos cooking), con un poco de investigacion, vemos que la función find_one, toma operadores como $eq, $gt, $lt, y $ne.&lt;/p&gt;

&lt;p&gt;Estos fitros se le pasan a la función find_one en forma de objetos de los campos, como&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"password": {"$lt": 5}}
/
variable["password"]["lt"] = 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bueno pues podemos usar $ne=null para que nos devuelva cualquiera que no sea nulo. Con esto y un bizcocho en teoría habríamos vulnerado la aplicacion.&lt;/p&gt;

&lt;p&gt;Ahora solo falta explotarla&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explotación&lt;/strong&gt;&lt;br&gt;
Para la fase de explotación, realizaremos lo siguiente:&lt;/p&gt;

&lt;p&gt;Interceptaremos la petición post de un login normal en BURP.&lt;br&gt;
Cambiaremos el valor de los campos user y password a los de (en formato json) a “password/login”:”$ne”:null, con su sintaxis adecuada.&lt;br&gt;
Además, para que la aplicacion acepte el json, cambiaremos el header de … url-encoded … a application/json. Consecuentemente dejaremos una linea de espacio entre los header de http y el inicio de nuestro nuevo JSON.&lt;br&gt;
Disfrutar de nuestra flag.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
