class Analyzer::Aspnet::WebForms

Overview

ASP.NET WebForms attack surface.

WebForms is file-path routed: an .aspx page's location under the web root is its URL. The parameters, however, mostly do not live in the page — they live in its code-behind and, more often, in the .ascx user controls the page registers. Across the validation corpus only ~19% of literal request reads sit in page code-behind and 54% sit in user controls, so a page-only scan would miss most of the surface. Control reads are therefore attributed to the pages that register them.

Defined in:

analyzer/analyzers/aspnet/webforms.cr

Constant Summary

ALIASED_COLLECTION_RE = /\b\w+\s*\.\s*(QueryString|Form|ServerVariables)\s*[\(\[]\s*"([^"]*)"\s*[\)\]]/i

Collection reads, in both indexer syntaxes. In every pattern below the closing delimiter must follow the literal immediately: Request("laauth-" & TabModuleId) builds its key at runtime, and capturing laauth- from it would invent 216 bogus params.

QueryString / Form / ServerVariables name nothing but an HTTP request, so any receiver is accepted. That is what catches aliased locals: kartris's Image.ashx assigns Dim req As HttpRequest = context.Request and then does ten reads through req, which a Request-anchored pattern would all miss.

BARE_REQUEST_RE = /\bRequest\s*[\(\[]\s*"([^"]*)"\s*[\)\]]/i
CLASS_DEFINITION_RE = /(?:^|\s)(?:Partial\s+|partial\s+)?(?:Public\s+|Friend\s+|public\s+|internal\s+)?(?:Class|class)\s+(\w+)/

Class declarations, used only to resolve services whose code lives away from the handler file.

CS_SIGNATURE_RE = /\b(?:public|internal)\s+(?:(?:static|virtual|override|async)\s+)*[\w<>\[\],.\s]+?\s+(\w+)\s*\(([^)]*)\)/
DESIGNER_FILE_RE = /\.designer\.(?:cs|vb)\z/i
DIRECTIVE_ATTR_RE = /([\w:.-]+)\s*=\s*"([^"]*)"/
DIRECTORY_INDEX = "default.aspx"
FRAMEWORK_FIELDS = Set {"__viewstate", "__viewstategenerator", "__viewstateencrypted", "__eventtarget", "__eventargument", "__eventvalidation", "__lastfocus", "__previouspage", "__asyncpost", "__scrollpositionx", "__scrollpositiony"}

WebForms postback plumbing, not user-facing parameters.

GLOBAL_ASAX = "global.asax"

Global.asax marks the application root, and unlike CFML's Application.cfc there is at most one per web app (verified across the corpus: 1 for kartris, 1 for DNN at DNN Platform/Website/, 0 for the two module-style repos), which makes it a trustworthy anchor rather than a guess.

PAGE_DIRECTIVE_RE = /<%@\s*(?:Page|Control|Master|WebHandler|WebService)\b([\s\S]*?)%>/i

Directives span multiple physical lines in real projects, so the whole <%@ ... %> block is captured rather than the first line.

PAGE_EXTENSIONS = [".aspx", ".ashx", ".asmx"]

Requestable handlers. .ascx (user control) and .master (master page) are composed into pages and are blocked by the default IIS handler mapping, so they are sources of params but never routes.

REGISTER_DIRECTIVE_RE = /<%@\s*Register\b([\s\S]*?)%>/i
REQUEST_COLLECTION_RE = /\bRequest\s*\.\s*(QueryString|Form|Params|Cookies|Headers|ServerVariables)\s*[\(\[]\s*"([^"]*)"\s*[\)\]]/i

Params / Cookies / Headers are ordinary member names — mail messages, HTTP clients and parsers all expose them — so these must be anchored to Request. \bRequest\. still covers every receiver chain (this., context., HttpContext.Current.).

SERVICE_EXTENSIONS = [".asmx", ".ashx"]
VB_SIGNATURE_RE = /\b(?:Public|Friend)\s+(?:(?:Overrides|Shared|Overloads|NotOverridable)\s+)*(?:Function|Sub)\s+(\w+)\s*\(([^)]*)\)/
WEBMETHOD_ATTR_RE = /(?:\[WebMethod[^\]]*\]|<WebMethod[^>]*>)/i

[WebMethod] / <WebMethod()> on an .asmx service maps to POST /Service.asmx/MethodName.

WEBMETHOD_WINDOW = 400
WEBROOT_MARKERS = ["wwwroot/"]

Instance Method Summary

Instance methods inherited from class Analyzer

analyze analyze, base_path : String base_path, base_paths : Array(String) base_paths, callees_needed? : Bool callees_needed?, http_header_name(name : String) : String | Nil http_header_name, line_number_for_index(content : String, char_index : Int32) : Int32 line_number_for_index, logger : NoirLogger logger, parallel_analyze(files : Array(String), &block : String -> Nil) parallel_analyze, read_file_content(path : String) : String read_file_content, result : Array(Endpoint) result, unique_params(params : Array(Param)) : Array(Param) unique_params, url : String url, web_root_path(path : String, markers : Array(String)) : String web_root_path

Constructor methods inherited from class Analyzer

new(options : Hash(String, YAML::Any)) new

Instance methods inherited from module FileHelper

all_files : Array(String) all_files, get_files_by_extension(extension : String) : Array(String) get_files_by_extension, get_files_by_extensions(extensions : Array(String)) : Array(String) get_files_by_extensions, get_files_by_prefix(prefix : String) : Array(String) get_files_by_prefix, get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String) get_files_by_prefix_and_extension, get_public_dir_files(base_path : String, folder : String) : Array(String) get_public_dir_files, get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String) get_public_files

Instance Method Detail

def analyze #

[View source]