Skip to content

Low-Privilege Users Can Bypass Authorization to Obtain LLM API Keys #9600

@LinYuanyi1

Description

@LinYuanyi1

Vulnerability Analysis

Some sensitive endpoints in AiragModelController are missing @RequiresPermissions authorization annotations. A normal authenticated user can access the model configuration export endpoint /airag/airagModel/exportXls and may export the credential field in the AiragModel entity. This field is used to store credentials for AI providers, such as API keys for OpenAI, DeepSeek, and others.

In addition, the /airag/airagModel/test endpoint is also missing permission protection. An attacker can craft a model configuration request body to trigger server-side calls to LLM, embedding, or image model APIs, causing unauthorized usage of model capabilities and quota consumption. It can also be used to verify whether a leaked API key is valid.

File: AiragModelController.java

@Tag(name = "AiRag model configuration")
@RestController
@RequestMapping("/airag/airagModel")
@Slf4j
public class AiragModelController extends JeecgController<AiragModel, IAiragModelService> {
    @Autowired
    private IAiragModelService airagModelService;

    @Autowired
    AIChatHandler aiChatHandler;

The controller base path is:

/airag/airagModel

Key Source 1: Protected add/edit endpoints for comparison

In the same controller, the add and edit endpoints do have permission annotations:

@PostMapping(value = "/add")
@RequiresPermissions("airag:model:add")
public Result<String> add(@RequestBody AiragModel airagModel) {
    AssertUtils.assertNotEmpty("模型名称不能为空", airagModel.getName());
    AssertUtils.assertNotEmpty("模型类型不能为空", airagModel.getModelType());
    AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
    if(oConvertUtils.isObjectEmpty(airagModel.getActivateFlag())){
        airagModel.setActivateFlag(0);
    }
    airagModelService.save(airagModel);
    return Result.OK("添加成功!");
}

@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
@RequiresPermissions("airag:model:edit")
public Result<String> edit(@RequestBody AiragModel airagModel) {
    airagModelService.updateById(airagModel);
    return Result.OK("编辑成功!");
}

This shows that the developer already treats model configuration as an administrative function that should be access-controlled. However, the later list, queryById, exportXls, and test endpoints are missing the same level of permission protection, creating an access control gap.

Key Source 2: Unauthorized export of model configuration

@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiragModel airagModel) {
    return super.exportXls(request, airagModel, AiragModel.class, "AiRag模型配置");
}

Reproduction

After obtaining a low-privilege user token, you can directly use the included poc.py to export the API key.

You can also test it with Burp:

Image

The exported table looks like this:

Image

The API key can be obtained.

Recommended Fixes

  1. Add permission annotations to sensitive endpoints:
@RequiresPermissions("airag:model:list")
@GetMapping(value = "/list")

@RequiresPermissions("airag:model:queryById")
@GetMapping(value = "/queryById")

@RequiresPermissions("airag:model:exportXls")
@RequestMapping(value = "/exportXls")

@RequiresPermissions("airag:model:test")
@PostMapping(value = "/test")
  1. Prevent the credential field from appearing in normal responses and export files:
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String credential;

or:

@JsonIgnore private String credential;
  1. Use a dedicated VO for Excel export instead of exporting the entity class AiragModel.class directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions