Robson Cruz
SECOMPP 2017
@deapyxel
Todo programa, em toda e qualquer plataforma é um alvo em potencial
Pessoas mal intencionadas irão tentar encontrar falhas no sua aplicação, servidor, script, etc.
Pessoas mal intencionadas irão utilizar toda e qualquer vulnerabilidade para obter informações, corromper dados, ganhar o controle de sistemas...
A propriedade e reputação sua e do seu cliente está sempre em jogo.
1
2
3
Segurança não é algo que pode simplesmente ser adicionado ao software como um plugin ou de maneira trivial.
Uma casa feita de papelão não se tornou mais segura por colocarmos um cadeado na porta.
Apple Developers, 2016
É impossível se prevenir contra todas as vulnerabilidades existentes, e muitas vezes, nem todas são pertinentes ao seu software.
Nosso trabalho é identificar as falhas que oferecem risco ao nosso software e incorporar práticas de codificação segura durante o desenvolvimento do projeto.
A prática de construir software levando em conta as vulnerabilidades, riscos e tecnologias disponíveis, de forma a mitigar e impedir a exploração e uso não intencionado do mesmo .
Manter a confiabilidade, integridade e disponibilidade das informações, garantindo uma operação de negócios de sucesso.
OWASP, 2010
Garantir que a informação seja revelada somente à partes autorizadas.
Garantir que a informação é precisa e válida, e que não houveram alteração por ações não autorizadas.
Garantir que a informação esteja disponível sempre que necessário.
C
I
D
Riscos são a combinação de fatores que ameaçam o sucesso do negócio
Um agente malicioso interage com o sistema, que pode possuir uma vulnerabilidade a ser explorada com o objetivo de causar impacto.
Fraquezas que tornam um sistema suscetível a ataques ou danos.
Utiliza do overflow para desviar o fluxo de execução do programa.
int somar (int a, int b) {
int res = a + b;
return res;
}
int main (int argc, char ** argv) {
int resultado = somar (1,2);
printf ("Resultado = %d\n", resultado);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char ** argv) {
char buf1 [16];
char buf2 [32];
printf("stack\n");
memset(buf1, '1', 16);
memset(buf2, '2', 32);
printf("buf1 = %s\n", buf1);
printf("buf2 = %s\n", buf2);
getch();
return 0;
}
$ ./stack2 10
stack
buf1 = 1111111111111111,
buf2 = 222222222222222222222222222222221111111111111111,
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char ** argv) {
char buf1 [16];
char buf2 [32];
printf ("stack\n");
memset (buf1, '1', 16);
memset (buf2, '2', 32 + atoi(argv[1]));
printf ("buf1 = %s\n", buf1);
printf ("buf2 = %s\n", buf2);
getch();
return 0;
}
$ ./stack2 10
stack
buf1 = 2222222222111111,
buf2 = 222222222222222222222222222222222222222222111111,
Utiliza do overflow para manipular o conteúdo de outros buffers.
A exploração depende muito do ambiente.
É comum ser precedido de um integer overflow.
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
int main (int argc, char ** argv) {
FILE *fd;
char *buf = (char *) malloc(8);
char *arquivo = (char *) malloc(16);
strcpy(arquivo, "/tmp/arquivo");
gets(buf);
printf("Abrindo o arquivo: %s\n", arquivo);
fd = fopen (arquivo, "w");
fputs(buf, fd);
fclose(fd);
return 0;
}
$ echo "teste heap" | ./heap
Abrindo o arquivo: /tmp/arquivo
$ cat /tmp/arquivo
teste heap
$ perl -e ’print "A"x15’ | ./heap
Abrindo o arquivo: /tmp/arquivo
// mesmo com buf tendo 8 bytes, o programa não apresenta problemas
$ perl -e ’print "A"x16’ | ./heap
Abrindo o arquivo: /tmp/arquivo
Segmentation Fault
// com 16 bytes, o nome do arquivo some por uma falha de segmentação
$ perl -e ’print "A"x20’ | ./heap
Abrindo o arquivo: AAAA
// com 17+ bytes, o nome do arquivo é sobrescrito
$ perl -e ’print "A"x16+"/etc/passwd"’ | ./heap
Abrindo o arquivo: /etc/passwd
Funções são consideradas inseguras por não permitir certas verificações, ou não possuir certos comportamentos restritivos.
Função | Risco | Sugestão |
---|---|---|
gets() | Altíssimo | fgets(buf, size, stdin) |
strcpy() | Alto | strncpy() |
strcat() | Alto | strncat() |
sprintf() | Alto | snprintf() e precisão do formato |
scanf() | Alto | Use precisão do formato |
Implementação da LibC, OpenBSD, 2005
Função utilizada para calcular o tamanho de uma string.
int main (int argc, char ** argv) {
char *exemplo = "quantidade de bytes";
printf("%d\n", strlen(exemplo));
return 0;
}
./strlen_test
19
O problema é a condição de parada: o terminador de string \0
int main (int argc, char ** argv) {
char a[32];
memset(a, 'a', 32);
printf("Tamanho de a = %d\n", strlen(a));
return 0;
}
$ ./problema_strlen
Tamanho de a = 49
O valor do tamanho de a pode variar dependendo do compilador, estado atual da máquina, etc.
int main (int argc, char ** argv) {
char a[33];
memset(a, 'a', 32);
a[32] = '\0';
printf("Tamanho de a = %d\n", strlen(a));
return 0;
}
$ ./solucao_strlen
Tamanho de a = 32
De forma geral, strlen() não é considerada uma função segura, e sua utilização deve ser evitada
$myvar = "varname";
$x = $_GET['arg'];
eval("\$myvar = \$x;");
Quando a função eval() recebe dados não confiáveis e os interpreta uma injeção de código seria possível.
/index.php?arg=1; phpinfo()
/index.php?arg=1; system('id')
<?php
$name = $_REQUEST['name'];
?>
<html>
<body>
Hello,
<?php
echo $name;
?>!
</body>
</html>
localhost/welcome.php?name=%3Cscript%3Ealert%2842%29;%3C/script%3E
O parâmetro name é ecoado de volta para o navegador, que irá executar o JS. O navegador confia que o JS foi enviado pelo servidor, mas esse não é o caso.
<?
$a = htmlentities($_GET['a']);
$b = $_GET['b'];
$c = $_GET['c'];
$d = htmlentities($b);
echo($a); //"seguro"
echo(htmlentities($b)); //"seguro"
echo($c); //XSS
echo($d); //"seguro"
echo(htmlentities($_GET['id']); //"seguro"
?>
Falhas desse tipo são comuns a todo código que trabalham do lado do servidor
<?php
$var = $_POST['var'];
mysql_query("SELECT * FROM sometable WHERE id = $var");
?>
Sem dúvida uma das vulnerabilidades mais simples de corrigir e menos corrigida.
<?php
$var = mysql_real_escape_string($_POST['var']);
?>
import hashlib
import time
import threading
def ipv4_md5_search(hash, range_start=0, range_end=256):
for a in range(range_start, range_end):
# show progress every time new value of a is done.
print("{} : {}".format(a, time.ctime()))
for b in range(256):
for c in range(256):
for d in range(256):
h = hashlib.md5(str('%s.%s.%s.%s' %
(a, b, c, d)).encode('utf-8')).hexdigest()
if h == hash:
print("{}.{}.{}.{}".format(a, b, c, d))
return True
print("No match found")
threads = []
for i in range(0, 256, 1):
t = threading.Thread(target=ipv4_md5_search,
args=('f39d1e9bce27c0f31f536a272e544a16', i, i + 1))
threads.append(t)
t.start()
Every program and every user of the system should operate using the least set of privileges necessary to complete the job.
Saltzer AND Schroeder
Um usuário comum deveria ter o privilégio para deletar arquivos temporário de sistema?
Imagine que João, um empresário chega ao banco e solicita a caixa que ela transfira X dólares da conta A para a conta B.
O caixa então simplesmente realiza a transação.
Sua aplicação
DB
Usuário
Bibliotecas
Requests
etc
Fronteira da Confiança
Não confiável
Rejeitado
Aceito
VALIDAÇÃO
Validação também é útil na identificação de bugs
Sua aplicação
DB
Usuário
Bibliotecas
Requests
etc
Integer
Integer
Validação
Sua aplicação
DB
Usuário
Bibliotecas
Requests
etc
Integer
Conta
public final class NumeroConta{
private final Integer valor;
public NumeroConta(Integer valor){
if(!isValid(valor)){
throw new IllegalOperationException("NumeroConta inválido!!");
}
this.valor = valor;
}
public static boolean isValid(Integer numeroConta){
return numeroConta != null &&
hasLength(numeroConta, 10, 12) &&
isNumeric(numeroConta);
}
}
public void Reticulate(Spline spline, int angle);
public void Reticulate(Spline spline, Angle angle);
public Temperature calcTemperature(int heat, int time);
public Temperature calcTemperature(Temperature heat, Time time);
public void Foo(string untrusted_bar){
if(!IsValid(untrusted_bar)){
throw new ValidationException();
}
var bar = untrusted_bar;
doSomethingWith(bar);
}
public void Foo(string bar){
if(!IsValid(bar)){
throw new ValidationException();
}
doSomethingWith(bar);
}
public void Foo(Unstrusted<string> bar);
public class Untrusted<T>{
readonly T _value;
public Untrusted(T value){
_value = value;
}
private T Value {
get{
return _value
};
}
}
[assembly: InternalsVisibleTo("Validation")]
Não "existe" um Getter!
public abstract class Validator<T>{
public T Validate(Untrusted<T> untrusted){
if(!InnerValidate(untrusted.Value)){
throw new ValidationException();
}
return untrusted.Value;
}
protected abstract bool InnerValidate(T value);
}
Enquanto isso, na unidade de Validação...
Agora podemos construir nossas próprias validações simplesmente por herdar essa classe
public void HandleAcctNbr(Untrusted<string> accountNbr){
var trusted = new AccountNumberValidator().Validate(accountNbr);
DoSomethingWith(trusted);
}
public void CreateAccount(string nbr){
var untrusted Nbr = new Untrusted<string>(nbr);
HandleAccountNbr(untrustedNbr);
...
}
//
//
// After the access check
symlink("/etc/passwd", "file");
// Before the open, "file" points to the password database
//
//
if (access("file", W_OK) != 0) {
exit(1);
}
fd = open("file", O_WRONLY);
write(fd, buffer, sizeof(buffer));
ToC
ToU
Anything that can possibly go wrong, does
Lei de Murphy
Seja pessimista em relação as possibilidades
boolean success = true;
...
return success;
boolean success = false;
...
return success;
Falhe rápido e garanta um caminho preciso para o sucesso.
public Resultado calculaResultado(Conta conta){
if(!possuiAcesso(conta))
throw new Exception();
...
return new Resultado(resultadoCalculadoNoCodigo);
}
Impossível sair sem objeto válido
Falhe rápido
Não existe ferramenta mágica.
Ocorre fora do tempo de execução, e é muito útil para detecção de bugs, já que analisa o código diretamente.
Ocorre em tempo de execução, e analisa o fluxo, memória e dados da aplicação, se mostrando útil para detectar vulnerabilidades que passam despercebidas
Imagine um projeto do tamanho de um sistema operacional
Consome mais tempo e é mais "cara"
Hoje iremos utilizar 2 ferramentas: VisualCodeGrepper e Acunetix WVS
Segurança
Privacidade
Personalização
Projeto mundial sem fins lucrativos que busca melhorar a segurança do software
https://www.owasp.org/index.php/Main_Page
Conferência que acontece no mundo todo, abordo diversos temas, incluindo segurança
https://blog.gotocon.com/
O Software Engineering Instutute(SEI) da Carnegie Mellon University possui muitos materiais publicados sobre a importância da codificação segura.
A divisão CERT do SEI possui várias noticias e publicações relativas a segurança da informação, assim como programas de treinamento.
https://wiki.sei.cmu.edu/confluence/display/seccode/Top+10+Secure+Coding+Practices