首页 网络安全 正文

simpleui端点认证缺失漏洞CVE-2026-16210

摘要

栋科技漏洞库关注到 simpleui 中存在的AjaxAdmin AJAX 端点认证缺失漏洞,现在已经被追踪为CVE-2026-16210,CVSS 3.X评分 7.3。

simpleui 是国内开源、基于 Vue+ElementUI 开发的Django Admin 后台美化主题,专门替换Django原生后台界面,无需改动业务代码。

一、基本情况

simpleui 是一款基于 Vue + Element UI 的 Django Admin 现代化主题开源组件,通过 pip 一键安装,仅需配置文件注册应用即可生效。

simpleui端点认证缺失漏洞CVE-2026-16210

simpleui 零侵入原有 Django 项目,原生后台增删改查、权限、过滤器全部保留可用,可解决原生 admin 界面老旧、操作单一的痛点。

栋科技漏洞库关注到 simpleui 中存在的AjaxAdmin AJAX 端点认证缺失漏洞,现在已经被追踪为CVE-2026-16210,CVSS 3.X评分 7.3。

二、漏洞分析

CVE-2026-16210是 simpleui 中存在的 AjaxAdmin AJAX 端点认证缺失,可能导致绕过Django管理员身份验证和操作权限的安全漏洞。

漏洞源于 simpleui.admin.AjaxAdmin 类在 Django Admin 中注册了 ajax 与 layer 自定义端点但未接入 Django Admin 认证与权限拦截。

由于AjaxAdmin注册自定义ajax、layer相关ModelAdmin路由时,未使用Django后台标准包装函数admin_site.admin_view()做鉴权包裹。

AJAX接口通过self.get_action(action)解析操作指令,直接基于POST请求参数构造数据集并执行对应操作;

全程未校验请求者是否登录、是否为后台员工、是否拥有对应模型访问权限、是否允许执行目标操作。

(一)受影响组件

simpleui/admin.py

AjaxAdmin.callback

AjaxAdmin.get_layer

AjaxAdmin.get_urls

(二)佐证代码证据

在提交哈希 85b887022217b4e46c9a34d227dbf38fef1e7a92 中,get_urls()直接追加原始视图可调用对象:

simpleui/admin.py 第177行:path("ajax", self.callback, name="%s_%s_ajax" % info)

simpleui/admin.py 第178行:path("layer", self.get_layer, name="%s_%s_layer" % info)

callback 函数会直接执行请求指定的操作:

simpleui/admin.py 第128行:仅判断当前Admin实例是否存在该操作方法

simpleui/admin.py 第129行:调用 self.get_action(action) 获取操作函数

simpleui/admin.py 第130行:基于请求参数构建查询集queryset

simpleui/admin.py 第131行:直接执行操作函数 func(self, request, qs)

前端界面会通过 admin.get_actions(request) 结合权限过滤展示按钮,但后端接口并未复用这套经过权限过滤的操作列表校验逻辑。

三、POC概念验证

(一)最简攻击场景

1、Django项目注册继承 simpleui.admin.AjaxAdmin 的模型管理类;

2、该模型后台注册了导出、批量修改或自定义高危批量操作;

3、攻击者向 /admin/<应用名>/<模型名>/ajax 发送POST请求,携带参数 _action=操作名、_selected=主键列表、select_across=0;

4. AjaxAdmin.callback() 直接解析并执行对应操作,完全绕过Django Admin原生身份认证与操作权限校验流程。

(二)漏洞危害

根据项目注册的后台操作类型,攻击者可未授权执行各类管理员操作,包含数据导出、数据批量修改、破坏性批量删除等高危行为。

(三)修复方案

1、在 get_urls() 中使用 self.admin_site.admin_view() 封装自定义AJAX视图;

2、执行操作或返回弹窗配置前,将请求操作名与 self.get_actions(request) 做比对,在服务端强制校验操作访问权限;

3、增加不安全HTTP方法拦截,完整保留Django Admin原生CSRF防护机制。

(四)参考资料

     def callback(self, request): 
         """ 
         This method is used to handle ajax requests. 
         """ 
         post = request.POST 
         action = post.get("_action") 

         # call admin 
         if hasattr(self, action): 
             func, action, description = self.get_action(action) 
             qs = self._get_queryset(request) 
             r = func(self, request, qs) 
             if r is None: 
                 return JsonResponse(data={ 
                     "status": "success", 
                     "msg": "Success!" 
                 }) 
             if isinstance(r, HttpResponseRedirect): 
                 return JsonResponse(data={ 
                     "status": "redirect", 
                     "url": r.url 
                 }) 
             elif isinstance(r, JsonResponse): 
                 return r 
             elif isinstance(r, dict): 
                 return JsonResponse(data=r) 
             else: 
                 logging.warning(f"action {action} return type is {type(r)}") 
                 return JsonResponse(data={"status": "error", "msg": r}) 

     def get_layer(self, request): 
         """ 
         This method is used to get the layer of the admin interface. 
         """ 
         _action = request.POST.get("_action") 
         if hasattr(self, _action): 
             func, action, description = self.get_action(_action) 
             if hasattr(func, "layer"): 
                 arg_count = func.layer.__code__.co_argcount 
                 if arg_count == 2: 
                     result = func.layer(self, request) 
                 elif arg_count == 3: 
                     # 兼容老版本 
                     qs = self._get_queryset(request) 
                     result = func.layer(self, request, qs) 

                 return JsonResponse(data=result, safe=False) 
         else: 
             raise Exception(f'action "{_action}" not found') 

     def get_urls(self): 
         """ 
         This method is used to add ajax functionality to the admin interface. 
         """ 
         info = self.model._meta.app_label, self.model._meta.model_name 

         return super().get_urls() + [ 
             path("ajax", self.callback, name="%s_%s_ajax" % info), 
             path("layer", self.get_layer, name="%s_%s_layer" % info) 
         ] 

四、影响范围

simpleui ≤ 2026.01.13

五、修复建议

simpleui>2026.01.13

六、参考链接

管理员已设置登录后刷新可查看



扫描二维码,在手机上阅读
评论
更换验证码
友情链接