<?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>Forem: arturo melgarejo</title>
    <description>The latest articles on Forem by arturo melgarejo (@arturo0x90).</description>
    <link>https://forem.com/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>Forem: arturo melgarejo</title>
      <link>https://forem.com/arturo0x90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/arturo0x90"/>
    <language>en</language>
    <item>
      <title>Baby-Cached WriteUp</title>
      <dc:creator>arturo melgarejo</dc:creator>
      <pubDate>Thu, 12 Feb 2026 15:10:46 +0000</pubDate>
      <link>https://forem.com/arturo0x90/baby-cached-writeup-ck5</link>
      <guid>https://forem.com/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://forem.com/arturo0x90/candyvault-writeup-fli</link>
      <guid>https://forem.com/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>
