| name | dotnet-reversing |
| description | Use when reverse engineering .NET assemblies, decompiling DLLs/EXEs, or hunting for vulnerabilities in .NET applications. |
.NET Reverse Engineering
Load the vuln-assessment-methodology skill alongside this one for severity
calibration, disprove-first discipline, and reporting standards.
Quick Start
dotnet_scan_binaries(path="/target") # find all .NET binaries
dotnet_list_namespaces(path="App.dll") # survey structure
dotnet_search_by_name(path="App.dll", search="password") # find interesting types/methods
dotnet_decompile_type(path="App.dll", type_name="App.AuthService") # read the code
Tools
| Tool | Purpose |
|---|
dotnet_scan_binaries(path, pattern?, exclude?) | Find .NET binaries. exclude is comma-separated patterns to skip. |
dotnet_list_namespaces(path) | List namespaces |
dotnet_list_types(path) / dotnet_list_types_in_namespace(path, namespace) | List types |
dotnet_list_methods(path) / dotnet_list_methods_in_type(path, type_name) | List methods |
dotnet_decompile_type(path, type_name) | Decompile a type to C# — preferred for targeted analysis |
dotnet_decompile_methods(path, method_names) | Decompile specific methods by name |
dotnet_decompile_module(path) | Decompile entire assembly — avoid, output is huge |
dotnet_search_by_name(path, search) | Find types/methods by name |
dotnet_search_references(path, search) | Find methods that call or use an API in IL bytecode |
dotnet_get_call_flows(paths, method_name, max_depth?) | Trace how a method is reached from entry points |
dotnet_download_nuget(package, version?, output_dir?) | Download NuGet package for analysis |
report_finding(file, method, criticality, content) | Report a finding. Criticality: critical/high/medium/low/info |
report_auth(auth_material) | Report hardcoded credentials, API keys, tokens |
report_poc(poc) | Save a proof-of-concept with exploitation steps |
finish_task(success, markdown_summary) | Mark task complete with summary |
Key difference: search_by_name finds things named "Sql", while search_references finds code that uses SqlCommand.
Vulnerability Hunting Workflow
Phase 1: Survey
dotnet_scan_binaries(path="/app")
dotnet_list_namespaces(path="Target.dll")
Identify the application structure. Focus on non-Microsoft assemblies.
Phase 2: Search for Security-Sensitive Patterns
Use dotnet_search_by_name for name-based searches and dotnet_search_references for IL bytecode/API usage searches. Run these across each target assembly.
Name searches (search_by_name): password, credential, secret, apikey, token, auth, encrypt, decrypt, hash, query, endpoint, url
API/IL reference searches (search_references):
- Deserialization (RCE):
BinaryFormatter, ObjectStateFormatter, NetDataContractSerializer, LosFormatter, JsonConvert.DeserializeObject, XmlSerializer, JavaScriptSerializer
- Command execution:
Process.Start, System.Diagnostics.Process, PowerShell, cmd.exe
- Crypto:
System.Security.Cryptography
- File I/O:
System.IO.File, FileStream, StreamReader, Path.Combine
- SQL:
SqlCommand, ExecuteNonQuery, ExecuteReader
- HTTP (SSRF):
HttpClient, WebRequest, HttpWebRequest
- XML (XXE):
XmlReader, XmlDocument, XDocument, XmlTextReader
- LDAP:
DirectorySearcher, DirectoryEntry, System.DirectoryServices
Phase 3: Decompile and Verify
dotnet_decompile_type(path="App.dll", type_name="App.Services.AuthenticationService")
Read the actual C# source. When you find a dangerous pattern, read the full
function and its callers before drawing conclusions. Check for:
- Hardcoded credentials
- Weak crypto (MD5, SHA1, DES, static IVs/keys)
- SQL string concatenation — but check if the concatenated value comes from
user input (HTTP param) vs config/env var. Only HTTP-sourced values are high severity.
- Unsanitized user input in file paths
- Dangerous deserialization
- Command injection — but check if the calling function has validation/filtering.
If validation exists, look for bypasses rather than reporting "no sanitization."
.NET-specific: JWT ReadToken is not always a finding
ReadToken/ReadJwtToken without ValidateToken is NOT a vulnerability when
the token is validated by a downstream service (Azure AD, ARM) or used only for
metadata extraction (expiry, caching). Only report it when unvalidated claims
drive authorization decisions.
Phase 4: Trace Attack Paths
dotnet_get_call_flows(
paths=["App.dll", "App.Core.dll"],
method_name="ExecuteCommand",
max_depth=10
)
Find how vulnerable methods are reached from entry points (controllers, handlers, public APIs).
Phase 5: Assess Severity and Report
Assign severity based on actual exploitability — not the vulnerability class
name. The vuln-assessment-methodology skill has the full guidance; the
essentials:
| Source of dangerous input | Access required | Severity |
|---|
| HTTP request parameter | Unauthenticated, internet-facing | Critical/High |
| HTTP request parameter | Authenticated user | High/Medium |
| HTTP request parameter | Internal network only | Medium |
| Config file / env var | Container or host access | Low |
| Hardcoded value (as sink input) | N/A | Not a finding (but hardcoded credentials are — see methodology skill) |
Before reporting every finding:
- Trace the data flow from attacker-controlled source to sink
- Actively try to disprove it — look for validation, encoding, authorization
- If defensive code exists, demonstrate a specific bypass or retract
- Verify severity reflects exploitability, not vulnerability class name
report_finding(
file="App.dll",
method="AuthService.ValidateToken",
criticality="critical",
content="Hardcoded JWT signing secret in source code:\n```csharp\nprivate static string Secret = \"supersecret123\";\n```"
)
report_auth(auth_material="API key in config: `sk-1234567890abcdef`")
report_poc(poc="## Exploitation\n1. Extract JWT secret\n2. Forge admin token\n3. ...")
finish_task(success=True, markdown_summary="Found 2 high-severity issues...")
Always report findings to persist them to the Dreadnode platform.
.NET Vulnerability Patterns: Vulnerable vs Safe
For each pattern, both vulnerable AND safe versions are shown. You must
check which one the code matches before reporting.
Hardcoded Credentials
private static string ApiKey = "sk-1234567890abcdef";
connectionString = "Server=db;User=admin;Password=P@ssw0rd";
var apiKey = Configuration["ApiKey"];
var connStr = Environment.GetEnvironmentVariable("DB_CONNECTION");
throw new Exception("Api Key is invalid. Subscription validation failed.");
Insecure Deserialization
BinaryFormatter formatter = new BinaryFormatter();
object obj = formatter.Deserialize(untrustedStream);
JsonConvert.DeserializeObject(json, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All
});
var obj = JsonSerializer.Deserialize<MyType>(json);
JsonConvert.DeserializeObject<MyType>(json);
Command Injection
Process.Start("cmd.exe", "/c " + userInput);
Arguments = $"-c \"{command} {string.Join(" ", args)}\"";
var error = ValidateCommand(command);
if (error != null) return error;
Process.Start("myapp", new[] { "--flag", sanitizedValue });
SQL Injection
string query = "SELECT * FROM users WHERE id = " + request.UserId;
string proc = "[" + schemaFromEnvVar + "].[MyProcedure]";
cmd.CommandText = "SELECT * FROM users WHERE id = @id";
cmd.Parameters.AddWithValue("@id", userId);
Blazor XSS (MarkupString)
builder.AddContent(0, (MarkupString)userInput);
var encoded = WebUtility.HtmlEncode(userInput);
var colored = AnsiParser.ConvertToHtml(encoded, state);
builder.AddContent(0, (MarkupString)colored);
pipeline.DisableHtml();
var html = Markdown.ToHtml(input, pipeline);
builder.AddContent(0, (MarkupString)html);
JWT Validation
var token = new JwtSecurityTokenHandler().ReadJwtToken(jwt);
if (token.Claims.First(c => c.Type == "role").Value == "admin")
GrantAdminAccess();
var token = handler.ReadJwtToken(jwt);
var expiry = token.ValidTo;
return DelegatedTokenCredential.Create(jwt);
Path Traversal
string path = Path.Combine(baseDir, userFileName);
File.ReadAllText(path);
string normalizedBase = Path.GetFullPath(baseDir) + Path.DirectorySeparatorChar;
string full = Path.GetFullPath(Path.Combine(baseDir, userFileName));
if (!full.StartsWith(normalizedBase)) throw new SecurityException();
Critical Rules
DO:
- Always start with
dotnet_scan_binaries to find targets
- Use
dotnet_decompile_type for targeted analysis (not dotnet_decompile_module)
- Report all verified findings with
report_finding — even low-severity ones
- Use
report_auth only for real credentials, not error messages or placeholders
- Call
finish_task when analysis is complete
DO NOT:
- Report
ReadToken/ReadJwtToken as "JWT bypass" when the token is validated server-side
- Report
MarkupString as XSS when the content is HtmlEncoded upstream
- Use
dotnet_decompile_module on large assemblies — it will overflow context
Tips
- Start narrow: Use
dotnet_decompile_type not dotnet_decompile_module — smaller output, faster analysis
- Search IL references:
dotnet_search_references finds actual usage in bytecode, not just type names
- Cross-assembly tracing:
dotnet_get_call_flows accepts multiple assemblies to trace calls across DLLs
- NuGet analysis: Download packages with
dotnet_download_nuget to analyze third-party dependencies
- Exclude noise: Use
exclude parameter in dotnet_scan_binaries to skip files, e.g. exclude="Microsoft.,System."
- Batch searches: Run multiple
search_references calls to cover all vulnerability classes before decompiling