simpleui端点认证缺失漏洞CVE-2026-16210
simpleui 是国内开源、基于 Vue+ElementUI 开发的Django Admin 后台美化主题,专门替换Django原生后台界面,无需改动业务代码。
一、基本情况
simpleui 是一款基于 Vue + Element UI 的 Django Admin 现代化主题开源组件,通过 pip 一键安装,仅需配置文件注册应用即可生效。

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
六、参考链接
管理员已设置登录后刷新可查看