乐于分享
好东西不私藏

开源AI工作流系统代码审计

开源AI工作流系统代码审计

1、前言

AI工作流系统在提升业务自动化效率的同时,其脚本执行、数据查询及权限控制等环节常存在高危缺陷。本文针对某开源系统进行代码审计,揭示攻击者可利用相关漏洞突破隔离边界、执行未授权操作、窃取核心数据甚至控制服务器,为同类平台的安全建设提供警示。

2、相关信息

spring-boot:3.5.9

mybatis-flex:1.11.6

3、代码审计流程

未授权漏洞

查看权限校验,Sa-Token的认证方式,拦截所有请求。

package tech.aiflowy.auth.config;  import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.stp.StpUtil; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  import javax.annotation.Resource;  @Configuration public class LoginAutoConfig implements WebMvcConfigurer {      @Resource     private LoginProperties properties;      @Resource     private NeedApiKeyInterceptor needApiKeyInterceptor;      @Override     public void addInterceptors(InterceptorRegistry registry) {         //创建Sa-Token拦截器,lambda表达式中调用StpUtil.checkLogin()检查用户是否已登录。         SaInterceptor saInterceptor = new SaInterceptor(handle -> {             StpUtil.checkLogin();         });         registry.addInterceptor(new CurdInterceptor())                 .order(101)                 .addPathPatterns("/**");         registry.addInterceptor(saInterceptor)                 .order(100)                   .addPathPatterns("/**")   //设置优先级为100(数字越小等级越高),拦截除excludePathPatterns方法设置的路径外的全部请求                 .excludePathPatterns("/")                 .excludePathPatterns("/error")                 .excludePathPatterns("/attachment/**")                 .excludePathPatterns("/api/v1/public/*")                 .excludePathPatterns("/api/v1/account/login")                 .excludePathPatterns("/api/v1/account/register")                 .excludePathPatterns("/thirdAuth/**")                 .excludePathPatterns("/public-api/**")                 .excludePathPatterns(properties.getExcludesOrEmpty());         registry.addInterceptor(needApiKeyInterceptor);     } }

在Sa-Token的认证机制下,以@SaIgnore用于声明一个接口或类不需要进行登录认证,示例如下,这里直接读取文件内容,但各种穿越符都尝试过了,不能穿越到重要文件的路径,这个以后再分析。

@SaIgnore     @GetMapping("/api/images/**")     public ResponseEntity<byte[]> getImage(HttpServletRequest request) throws IOException {         // 获取完整的路径         String fullPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);         String basePath = "/api/images/";         String filePath = fullPath.substring(basePath.length()-1);         String imagePath = getRootPath() + filePath;         File imageFile = new File(imagePath);          if (!imageFile.exists()) {             return new ResponseEntity<>(HttpStatus.NOT_FOUND);         }          // 读取文件内容         byte[] fileContent = Files.readAllBytes(imageFile.toPath());          // 返回文件内容         return ResponseEntity.ok()                 .header(HttpHeaders.CONTENT_TYPE, getContentType(imageFile))                 .body(fileContent);     }

寻找有价值的未授权端口,达到前台rce,/api/temp-token/create能够匿名创建临时token,但是经过测试每个功能点都会经过相对应的鉴权

代码执行

这里采取黑白盒的方式:本地搭建,查看对应功能点,寻找那种可以执行命令的功能点,再去查看相关的代码。(这也是新手最容易出洞的方式,其实也可以通过ai去审计全部代码,比较不好一点的是,ai太过大范围的搜索,反而会一无所获,如果核心代码逻辑打包成jar进行加载的话,ai不会主动去反编译)

我觉得这个时代下的代码审计,不必像传统的代码审计,通过简单的审计,反复调试,来寻找漏洞,但在深层次方面的漏洞,也不会普通的一句话,便让ai轻轻松松审计出来,更高效的方式,大概是告诉ai大致的审计思路、审计路线,让ai替你去实践。

审计流程如下:

GraalVM js 引擎,相关配置:参考文章:GraalVM Polyglot 沙箱逃逸:跨语言上下文 RCE-先知社区

static {         CONTEXT_BUILDER = Context.newBuilder(new String[]{"js"}).option("engine.WarnInterpreterOnly", "false").allowHostAccess(HostAccess.ALL).allowHostClassLookup((className) -> false).option("js.ecmascript-version", "2021");     }

1、HostAccess.ALL定义为允许 JS 访问所有注入 Java 对象的全部 public 方法和字段,包括 Object.getClass()、Class.getClassLoader() 等反射入口方法,这些方法是 JDK 基础类提供的,无法被 allowHostClassLookup 拦截。

2、className -> false永远返回 false,意图是禁止 Java.type("xxx")直接加载类。对通过已有对象实例调用 getClassLoader().loadClass()的反射路径完全无效。

效果:

后台sql注入漏洞

项目为 Spring Boot 3.5.9 多模块 Maven 工程,持久层框架为 MyBatis-Flex(mybatis-flex-spring-boot3-starter)

MyBatis-Flex 的危险 API 与原生 MyBatis 有所不同,审计重点锁定:

危险模式
说明
QueryWrapper.where(String)
接受原生 SQL 片段,不做参数化
QueryWrapper.orderBy(String)
ORDER BY 子句直接拼接
Db.selectListBySql(String, ...)
完全手写 SQL
XML/注解中的 ${}
MyBatis 字符串替换(非参数化)

搜索 ${} 拼接(MyBatis 字符串替换)

grep -rn "\${" --include="*.xml" --include="*.java"

搜索 wrapper.where(调用)

grep -rn "wrapper\.where(" --include="*.java"

搜索 orderBy(直接拼接)

grep -rn "\.orderBy(" --include="*.java"

可查找到src/main/java/tech/aiflowy/datacenter/service/impl/DatacenterTableServiceImpl.java,wrapper.where(condition)无任何处理,只需查询什么方法可以调用DatacenterQuery对象即可

private void buildCondition(QueryWrapper wrapper, DatacenterQuery where) {         // 构建查询条件         String condition = where.getWhere();         if (StrUtil.isNotEmpty(condition)) {             wrapper.where(condition);         }     }
grep -rn "DatacenterQuery" --include="*.java"

就有:src/main/java/tech/aiflowy/admin/controller/datacenter/DatacenterTableController.java

@GetMapping("/getPageData")     @SaCheckPermission("/api/v1/datacenterTable/query")     public Result<Page<Row>> getPageData(DatacenterQuery where) {         Page<Row> res = service.getPageData(where);         return Result.ok(res);     }

payload构造:src/main/java/tech/aiflowy/common/entity/DatacenterQuery.java设置了需要的参数pageNumber、pageSize、tableId、where(注入点):

package tech.aiflowy.common.entity; import java.math.BigInteger;  public class DatacenterQuery {      private Long pageNumber;     private Long pageSize;     // 表ID     private BigInteger tableId;     // 工作流传过来的查询条件     private String where;     public Long getPageNumber() {         return pageNumber;     }     public void setPageNumber(Long pageNumber) {         this.pageNumber = pageNumber;     }     public Long getPageSize() {         return pageSize;     }     public void setPageSize(Long pageSize) {         this.pageSize = pageSize;     }     public BigInteger getTableId() {         return tableId;     }     public void setTableId(BigInteger tableId) {         this.tableId = tableId;     }     public String getWhere() {         return where;     }     public void setWhere(String where) {         this.where = where;     } }

需要在相应的模块中创建数据表:

效果:

原文链接:https://www.freebuf.com/articles/web/483831.html

相关学习资料