Write up SHX 3 (PHP Security) - Omni Corp
Para quem não está familiarizado com competições de segurança da informação no estilo CTF (Capture The Flag), um “Write Up” é uma descrição de uma solução para um desafio de um CTF especifico. Caso nunca tenha ouvido falar de CTF, você pode começar por esse link.
Este Write Up é de um challenge do Shellter Labs que é uma rede social focada no aprendizado de segurança da informação e que periodicamente lança um conjunto de challenges com um assunto especifico, e nesta semana lançaram com o assunto de Segurança com PHP.
Descrição do problema:
“We were able to find a piece of this website code on the internet. We hope it help us to find a vulnerability that show us the secret code.” (Fomos capazes de encontrar um pedaço deste código do site na internet. Esperamos que nos ajude a encontrar uma vulnerabilidade que nos mostre o código secreto)
Junto ao problema, é fornecido o seguinte código-fonte:
<?php
include('includes/flag.php');
session_start();
?>
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Omni Cosimer Corp.</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<style>
/* NOTE: The styles were added inline because Prefixfree needs access to your styles and they must be inlined if they are on local disk! */
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
.btn { display: inline-block; *display: inline; *zoom: 1; padding: 4px 10px 4px; margin-bottom: 0; font-size: 13px; line-height: 18px; color: #333333; text-align: center;text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; background-color: #f5f5f5; background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); background-image: linear-gradient(top, #ffffff, #e6e6e6); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#ffffff, endColorstr=#e6e6e6, GradientType=0); border-color: #e6e6e6 #e6e6e6 #e6e6e6; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border: 1px solid #e6e6e6; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); cursor: pointer; *margin-left: .3em; }
.btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { background-color: #e6e6e6; }
.btn-large { padding: 9px 14px; font-size: 15px; line-height: normal; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; }
.btn:hover { color: #333333; text-decoration: none; background-color: #e6e6e6; background-position: 0 -15px; -webkit-transition: background-position 0.1s linear; -moz-transition: background-position 0.1s linear; -ms-transition: background-position 0.1s linear; -o-transition: background-position 0.1s linear; transition: background-position 0.1s linear; }
.btn-primary, .btn-primary:hover { text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); color: #ffffff; }
.btn-primary.active { color: rgba(255, 255, 255, 0.75); }
.btn-primary { background-color: #4a77d4; background-image: -moz-linear-gradient(top, #6eb6de, #4a77d4); background-image: -ms-linear-gradient(top, #6eb6de, #4a77d4); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#6eb6de), to(#4a77d4)); background-image: -webkit-linear-gradient(top, #6eb6de, #4a77d4); background-image: -o-linear-gradient(top, #6eb6de, #4a77d4); background-image: linear-gradient(top, #6eb6de, #4a77d4); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#6eb6de, endColorstr=#4a77d4, GradientType=0); border: 1px solid #3762bc; text-shadow: 1px 1px 1px rgba(0,0,0,0.4); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.5); }
.btn-primary:hover, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { filter: none; background-color: #4a77d4; }
.btn-block { width: 100%; display:block; }
* { -webkit-box-sizing:border-box; -moz-box-sizing:border-box; -ms-box-sizing:border-box; -o-box-sizing:border-box; box-sizing:border-box; }
html { width: 100%; height:100%; overflow:hidden; }
body {
width: 100%;
height:100%;
font-family: 'Open Sans', sans-serif;
background: #092756;
background: -moz-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%),-moz-linear-gradient(top, rgba(57,173,219,.25) 0%, rgba(42,60,87,.4) 100%), -moz-linear-gradient(-45deg, #670d10 0%, #092756 100%);
background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -webkit-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -webkit-linear-gradient(-45deg, #670d10 0%,#092756 100%);
background: -o-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -o-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -o-linear-gradient(-45deg, #670d10 0%,#092756 100%);
background: -ms-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -ms-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -ms-linear-gradient(-45deg, #670d10 0%,#092756 100%);
background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), linear-gradient(to bottom, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), linear-gradient(135deg, #670d10 0%,#092756 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3E1D6D', endColorstr='#092756',GradientType=1 );
}
.login {
position: absolute;
top: 50%;
left: 50%;
margin: -150px 0 0 -150px;
width:300px;
height:300px;
}
.login h1 { color: #fff; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; }
.login h3 { color: #fff; text-shadow: 0 0 10px rgba(1,0,0,0.3); letter-spacing:1px; text-align:center; }
.login a { color: #fff; font-size: 10px}
.head {
position: absolute;
top: 30%;
left: 43%;
margin: -150px 0 0 -150px;
width:500px;
height:500px;
}
.head h1 { color: #f8a; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; }
input {
width: 100%;
margin-bottom: 10px;
background: rgba(0,0,0,0.3);
border: none;
outline: none;
padding: 10px;
font-size: 13px;
color: #fff;
text-shadow: 1px 1px 1px rgba(0,0,0,0.3);
border: 1px solid rgba(0,0,0,0.3);
border-radius: 4px;
box-shadow: inset 0 -5px 45px rgba(100,100,100,0.2), 0 1px 1px rgba(255,255,255,0.2);
-webkit-transition: box-shadow .5s ease;
-moz-transition: box-shadow .5s ease;
-o-transition: box-shadow .5s ease;
-ms-transition: box-shadow .5s ease;
transition: box-shadow .5s ease;
}
input:focus { box-shadow: inset 0 -5px 45px rgba(100,100,100,0.4), 0 1px 1px rgba(255,255,255,0.2); }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
</head>
<body>
<div class="head">
<h1>OMNI COSIMER CORP.</h1>
</div>
<?php
// Check if user is authenticated
if( (isset($_SESSION['username']) == true)){
// Get user data
$id = $_SESSION['id'];
// Check if user logged user is admin
if ($id == 0){
echo $FLAG;
$username = 'Admin';
}
else{
$username = $_SESSION['username'];
}
?>
<div class="login">
<h1>Welcome, <?php echo $username; ?></h1>
<h3><a href="logout.php">Logout</a></h3>
</div>
<?php
}
else{
include 'includes/form.html';
}
?>
</body>
</html>
Também é oferecido um link para o site a ser explorado, e ao acessar o site, vemos isto:
Solução
Como nos CTFs o que interessa são as Flags, é possível ver no código fornecido que a parte que nos interessa para ser executada é essa:
<?php
// Check if user is authenticated
if( (isset($_SESSION['username']) == true)){
// Get user data
$id = $_SESSION['id'];
// Check if user logged user is admin
if ($id == 0){
echo $FLAG;
$username = 'Admin';
}
Tendo em mente que o código do formulário de login é este:
<form method="post" action="login.php" name="login">
<input name="username" placeholder="Username" id="username" required="required" type="text">
<input name="passwd" id="passwd" placeholder="Password" required="required" type="password">
<button type="submit" class="btn btn-primary btn-block btn-large">Let me in.</button>
<input name="id" id="id" value="1" type="hidden">
</form>
É natural que a primeira tentativa seja alterar o value do input “id” para 0, visto que é essa a comparação feita no código php, entretanto, ao fazer isto é retornado o seguinte erro:
Então, percebi que a vulnerabilidade trata-se de um erro comum cometido por programadores em linguagens de tipagem dinâmica como o PHP, que é não usar o “===” que além de comparar o conteúdo da variavel, compara o tipo. Para resolver explorar a falha, alterei o value do campo id para “false”:
Após submeter o formulário, conseguimos a flag:
Em PHP, as comparações false == 0
e true == 1
retornam true
, então é preciso muito cuidado ao desenvolver.
Caso tenha alguma dúvida ou algo a dizer, comente abaixo!