0xd00 随笔小记

Back

1. SQL注入#

Sink点#

  • SqlCommand.ExecuteNonQuery()
  • SqlCommand.ExecuteReader()
  • SqlCommand.ExecuteScalar()
  • SqlDataAdapter.Fill()
  • DbContext.Database.ExecuteSql()
  • DbContext.Database.SqlQuery()
  • Dapper connection.Query()
  • FromSqlRaw()
  • FromSqlInterpolated()

审计检查项#

  • 是否使用参数化查询(占位符@parameter)
  • SQL字符串是否通过拼接构造
  • 用户输入是否直接进入SQL
  • 参数是否通过Parameters.AddWithValue()绑定
  • 所有用户输入是否都被参数化处理
  • EF Core FromSql()FromSqlRaw()区别
  • 特殊子句(ORDER BY)是否有白名单
  • 存储过程参数是否验证

风险代码模式#

// 模式1:直接拼接
string username = Request.QueryString["username"];
string sql = "SELECT * FROM Users WHERE Name = '" + username + "'";
var cmd = new SqlCommand(sql, connection);
var result = cmd.ExecuteReader();
csharp
// 模式2:EF Core FromSqlRaw使用字符串插值
string filter = Request.QueryString["filter"];
var users = context.Users
    .FromSqlRaw($"SELECT * FROM Users WHERE Name = {filter}")
    .ToList();
csharp
// 模式3:ORM框架拼接SQL
var users = db.Queryable<User>()
    .Select(x => SqlFunc.MappingColumn("SELECT * FROM Users WHERE Name = '" + userInput + "'"))
    .ToList();
csharp

安全实现#

// 方案1:参数化查询
string username = Request.QueryString["username"];
string sql = "SELECT * FROM Users WHERE Name = @username";
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
    cmd.Parameters.AddWithValue("@username", username);
    var reader = cmd.ExecuteReader();
}
csharp
// 方案2:EF Core参数化
var username = Request.QueryString["username"];
var users = await context.Users
    .FromSql($"SELECT * FROM Users WHERE Name = {username}")
    .ToListAsync();
csharp
// 方案3:LINQ避免手写SQL
var username = Request.QueryString["username"];
var users = context.Users
    .Where(u => u.Name == username)
    .ToList();
csharp
// 方案4:ORDER BY白名单
string orderBy = Request.QueryString["orderBy"] ?? "id";
var allowedFields = new[] { "id", "name", "email", "createdAt" };
if (!allowedFields.Contains(orderBy))
    throw new ArgumentException("Invalid field");
var sql = "SELECT * FROM Users ORDER BY " + orderBy;
csharp

2. 命令执行#

Sink点#

  • Process.Start()
  • ProcessStartInfo.FileName
  • ProcessStartInfo.Arguments
  • ProcessStartInfo.UserName
  • ProcessStartInfo.Password

审计检查项#

  • 用户输入是否直接作为FileName
  • Arguments是否包含用户输入
  • UseShellExecute是否为true
  • 是否使用了绝对路径
  • 是否实现了命令白名单
  • 参数是否进行了验证

风险代码模式#

// 模式1:用户输入作为命令
string cmd = Request.QueryString["cmd"];
Process.Start(cmd);
csharp
// 模式2:通过shell执行
string hostname = Request.QueryString["hostname"];
Process.Start("cmd.exe", "/c ipconfig " + hostname);
csharp
// 模式3:ProcessStartInfo配置不当
var psi = new ProcessStartInfo
{
    FileName = "powershell.exe",
    Arguments = "-Command " + userCmd,
    UseShellExecute = true  // 通过shell执行,更危险
};
Process.Start(psi);
csharp

安全实现#

// 方案:命令白名单
var userCmd = Request.QueryString["cmd"];
var allowedCommands = new[] { "whoami", "date", "pwd" };

if (!allowedCommands.Contains(userCmd))
    throw new ArgumentException("Command not allowed");

Process.Start(userCmd);
csharp

3. 文件上传和任意文件写入#

Sink点#

  • File.SaveAs()
  • File.WriteAllBytes()
  • File.WriteAllText()
  • FileStream.Write()
  • File.Create()
  • StreamWriter.Write()
  • Path.Combine()

审计检查项#

  • 扩展名是否进行白名单验证
  • 是否防止了.aspx.ashx等可执行文件
  • MIME类型是否经过验证
  • 文件名是否包含路径分隔符
  • 是否使用Path.GetFileName()清理
  • 是否生成了新文件名
  • 保存路径是否在预期目录内
  • 最终路径是否被规范化验证

风险代码模式#

// 模式1:直接使用上传文件名
if (Request.Files.Count > 0)
{
    var file = Request.Files[0];
    file.SaveAs(Path.Combine(Server.MapPath("~/uploads"), file.FileName));
}
csharp
// 模式2:仅检查扩展名
if (Path.GetExtension(file.FileName).ToLower() == ".jpg")
{
    file.SaveAs("uploads/" + file.FileName);
}
csharp
// 模式3:未验证保存路径
var uploadPath = "uploads/" + file.FileName;
file.SaveAs(uploadPath);
csharp

安全实现#


4. 反序列化漏洞#

Sink点#

  • BinaryFormatter.Deserialize()
  • SoapFormatter.Deserialize()
  • NetDataContractSerializer.ReadObject()
  • LosFormatter.Deserialize()
  • ObjectStateFormatter.Deserialize()
  • JsonConvert.DeserializeObject()

审计检查项#

  • 是否对不可信输入执行反序列化
  • 数据来源是否可信(HTTP、Cookie)
  • 是否实现了类白名单
  • JsonConvertTypeNameHandling配置
  • 是否存在自定义readObject方法
  • 是否认识ViewState风险

风险代码模式#

// 模式1:直接反序列化用户输入
byte[] data = Convert.FromBase64String(Request.QueryString["data"]);
var formatter = new BinaryFormatter();
object obj = formatter.Deserialize(new MemoryStream(data));
csharp
// 模式2:LosFormatter反序列化
string viewState = Request.Form["__VIEWSTATE"];
var formatter = new LosFormatter();
object obj = formatter.Deserialize(viewState);
csharp
// 模式3:JsonConvert不安全配置
var settings = new JsonSerializerSettings 
{
    TypeNameHandling = TypeNameHandling.All
};
var obj = JsonConvert.DeserializeObject(userInput, settings);
csharp

安全实现#

// 方案1:使用安全的JsonConvert配置
var settings = new JsonSerializerSettings 
{
    TypeNameHandling = TypeNameHandling.None
};
var user = JsonConvert.DeserializeObject<User>(userInput, settings);
csharp
// 方案2:使用System.Text.Json
var options = new JsonSerializerOptions 
{
    TypeInfoResolver = null
};
var user = JsonSerializer.Deserialize<User>(userInput, options);
csharp
// 方案3:ViewState安全配置
// web.config中:
// <pages enableViewStateMac="true" viewStateEncryptionMode="Always" />
csharp

5. 任意文件读取#

Sink点#

  • File.ReadAllBytes()
  • File.ReadAllText()
  • Response.WriteFile()
  • Response.TransmitFile()
  • FileStream构造函数
  • StreamReader构造函数

审计检查项#

  • 用户参数是否直接作为文件路径
  • 是否使用Path.GetFileName()清理
  • 是否使用Path.GetFullPath()规范化
  • 最终路径是否在允许目录内
  • 是否实现了文件白名单

安全实现#


6. 路径遍历#

Sink点#

  • 所有文件操作

审计检查项#

  • 路径是否包含.../等特殊序列
  • 是否使用Path.GetFullPath()规范化
  • 规范化后路径是否在基础目录内

安全实现#

public string ValidatePath(string userInput)
{
    string baseDir = Path.GetFullPath("data/");
    string fullPath = Path.Combine(baseDir, userInput);
    string canonicalPath = Path.GetFullPath(fullPath);
    
    if (!canonicalPath.StartsWith(baseDir))
        throw new ArgumentException("Path traversal detected");
    
    return canonicalPath;
}
csharp

7. XXE (XML External Entity)#

Sink点#

  • XmlDocument.LoadXml()
  • XmlDocument.Load()
  • XmlReader.Create()
  • DataSet.ReadXml()

审计检查项#

  • 是否对不可信XML进行解析
  • DTD处理是否禁用
  • XmlResolver是否为null
  • 是否设置XIncludeAware=false

安全实现#

var settings = new XmlReaderSettings
{
    DtdProcessing = DtdProcessing.Prohibit,
    XmlResolver = null
};
using (XmlReader reader = XmlReader.Create(xmlStream, settings))
{
    var doc = new XmlDocument();
    doc.Load(reader);
}
csharp

8. SSRF (Server-Side Request Forgery)#

Sink点#

  • WebClient.DownloadString()
  • WebClient.DownloadData()
  • HttpClient.GetAsync()
  • HttpClient.PostAsync()
  • WebRequest.Create()

审计检查项#

  • 是否允许用户指定URL
  • 是否检测内部地址(127.0.0.1、localhost)
  • 是否限制了协议(仅HTTP/HTTPS)
  • 是否防止了DNS重绑定
  • 域名是否在白名单内

安全实现#


9. 其他漏洞#

XSS#

Sink点Response.Write()

检查项:是否对输出进行HTML编码

LDAP注入#

Sink点DirectorySearcher.Filter

检查项:是否对LDAP过滤器参数化

表达式注入#

Sink点System.Linq.Dynamic

检查项:是否允许用户提供表达式


10. 审计工具#

  • 静态分析:CodeQL、Semgrep、SonarQube、Fortify
  • 动态检测:OWASP ZAP、Burp Suite
  • 依赖检查:NuGet Audit、Snyk
C# 代码审计 - 漏洞Sink点
https://blog.0xd00.com/blog/csharp_audit
Author 0xd00
Published at 2025年12月18日
Comment seems to stuck. Try to refresh?✨