-
Notifications
You must be signed in to change notification settings - Fork 42
Add ASP.NET State Helpers #483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
terrorbyte
wants to merge
5
commits into
main
Choose a base branch
from
aspnet-state
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b86ee95
Add ASP.NET State Helpers
terrorbyte fe5a0c1
Update aspnet/aspnet.go
j-baines 8e25664
Update aspnet/aspnet.go
j-baines 92be04a
Fix parameter mix up. Thanks robot.
terrorbyte 9ae2aeb
Merge branch 'aspnet-state' of github.com:vulncheck-oss/go-exploit in…
terrorbyte File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| // Package aspnet provides helper functions to deal with ASP.NET and C# applications that utilize the state preserving hidden fields. These are notoriously annoying to automate and require multiple requests per action and often simulate user interaction clicks. The ASPState type helps speed up development of those requests. | ||
| // | ||
| // The package can be used to facility chains of go-exploit requests to ASP.NET applications like so: | ||
| // | ||
| // state := aspnet.State{} | ||
| // resp, body, ok := protocol.HTTPSendAndRecvWith("GET", conf.GenerateURL("/management/AdminDatabase.aspx"), "") | ||
| // if !ok { | ||
| // output.PrintError("Could not retrieve to the admin database endpoint") | ||
| // | ||
| // return false | ||
| // } | ||
| // | ||
| // state.Update(body) | ||
| // | ||
| // // Now only the parameters that are required can be utilized and no special body parsing | ||
| // // for __VIEWSTATE and friends is required. | ||
| // p := state.MergeParams(map[string]string{ | ||
| // "__EVENTTARGET": "ctl00$MainContent$DatabaseType", | ||
| // "ctl00%24MainContent%24DatabaseType": "psql", | ||
| // }) | ||
| // params := protocol.CreateRequestParamsEncoded(p) | ||
| // headers["Content-Type"] = "application/x-www-form-urlencoded" | ||
| // resp, body, ok = protocol.HTTPSendAndRecvWithHeaders("POST", conf.GenerateURL("/management/AdminDatabase.aspx"), params, headers) | ||
| // if !ok { | ||
| // output.PrintError("Could not POST to the admin database endpoint") | ||
| // | ||
| // return false | ||
| // } | ||
| // | ||
| // // Update the state from the previous POST response, this time we only want the states and have no content | ||
| // state.Update(body) | ||
| // params := protocol.CreateRequestParamsEncoded(state.AsParams()) | ||
| // resp, body, ok := protocol.HTTPSendAndRecvWithHeaders("POST", conf.GenerateURL("/management/AdminDatabase.aspx"), params, headers) | ||
| // if !ok { | ||
| // output.PrintError("Could not POST to the admin database endpoint") | ||
| // | ||
| // return false | ||
| // } | ||
| package aspnet | ||
|
|
||
| import ( | ||
| "maps" | ||
| "strings" | ||
|
|
||
| "github.com/antchfx/htmlquery" | ||
j-baines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
|
|
||
| // State represents the current state of the steps in a request chain for a ASP.NET application. The state should have all possible ASP.NET common state values represented and if they are not set in the current request state will be nil. This state structt only covers __VIEWSTATE, __VIEWSTATEGENERATOR, __EVENTVALIDATION, __EVENTARGUMENT, __EVENTTARGET, and __LASTFOCUS. The __EVENTTARGET and __EVENTARGUMENT are purposefully not omitted as there are often multiple or non-state required targets, so ensure they are set to the specific target. | ||
j-baines marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| type State struct { | ||
| ViewState *string | ||
| ViewStateGenerator *string | ||
| EventTarget *string | ||
| EventValidation *string | ||
| EventArgument *string | ||
| LastFocus *string | ||
| } | ||
|
|
||
| // xPathQuiet is similar to search.XPath, but does not trigger framework errors as these can be expected to be empty. | ||
| func xPathQuiet(document, path string) (string, bool) { | ||
| doc, err := htmlquery.Parse(strings.NewReader(document)) | ||
| if err != nil { | ||
| return "", false | ||
| } | ||
| n := htmlquery.FindOne(doc, path) | ||
| if n == nil { | ||
| return "", false | ||
| } | ||
|
|
||
| return htmlquery.InnerText(n), true | ||
| } | ||
|
|
||
| // AsParams creates a map structure for use with the protocol package HTTP helpers or in their raw map form. If the last process state did not have one of the parameters it will not be set, but empty string values are preserved. | ||
| func (state *State) AsParams() map[string]string { | ||
| u := map[string]string{} | ||
| if state.ViewState != nil { | ||
| u["__VIEWSTATE"] = *state.ViewState | ||
| } | ||
| if state.ViewStateGenerator != nil { | ||
| u["__VIEWSTATEGENERATOR"] = *state.ViewStateGenerator | ||
| } | ||
| if state.EventValidation != nil { | ||
| u["__EVENTVALIDATION"] = *state.EventValidation | ||
| } | ||
| if state.EventArgument != nil { | ||
| u["__EVENTARGUMENT"] = *state.EventArgument | ||
| } | ||
| if state.EventTarget != nil { | ||
| u["__EVENTTARGET"] = *state.EventTarget | ||
| } | ||
| if state.LastFocus != nil { | ||
| u["__LASTFOCUS"] = *state.LastFocus | ||
| } | ||
|
|
||
| return u | ||
| } | ||
|
|
||
| // MergeParams merges the hand written or custom parameters and the ASP.NET state parameters to allow for a single call to protocol.CreateRequestParamsEncoded for both the current state and any modifications that are necessary. The same rules for parameter empty vs not found exist as AsParams. The parameters passed in the function will override the underlying state values if they are passed. | ||
| func (state *State) MergeParams(p map[string]string) map[string]string { | ||
| params := state.AsParams() | ||
| maps.Copy(params, p) | ||
|
|
||
| return params | ||
| } | ||
|
|
||
| // Update the State to extract the supported state values and reset the parameters that are not found. This should be called after each HTTP request that requires state updates. This update only works on the first matched state document and if multiple states are set on the expected page manual updating may be required. | ||
| func (state *State) Update(body string) { | ||
| v, hasMatch := xPathQuiet(body, `//input[@name="__VIEWSTATE"]/@value`) | ||
| if hasMatch { | ||
| state.ViewState = &v | ||
| } else { | ||
| state.ViewState = nil | ||
| } | ||
| vg, hasMatch := xPathQuiet(body, `//input[@name="__VIEWSTATEGENERATOR"]/@value`) | ||
| if hasMatch { | ||
| state.ViewStateGenerator = &vg | ||
| } else { | ||
| state.ViewStateGenerator = nil | ||
| } | ||
| ev, hasMatch := xPathQuiet(body, `//input[@name="__EVENTVALIDATION"]/@value`) | ||
| if hasMatch { | ||
| state.EventValidation = &ev | ||
| } else { | ||
| state.EventValidation = nil | ||
| } | ||
| et, hasMatch := xPathQuiet(body, `//input[@name="__EVENTTARGET"]/@value`) | ||
| if hasMatch { | ||
| state.EventTarget = &et | ||
| } else { | ||
| state.EventTarget = nil | ||
| } | ||
| ea, hasMatch := xPathQuiet(body, `//input[@name="__EVENTARGUMENT"]/@value`) | ||
| if hasMatch { | ||
| state.EventTarget = &ea | ||
| } else { | ||
| state.EventTarget = nil | ||
terrorbyte marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| lf, hasMatch := xPathQuiet(body, `//input[@name="__LASTFOCUS"]/@value`) | ||
| if hasMatch { | ||
| state.LastFocus = &lf | ||
| } else { | ||
| state.LastFocus = nil | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| package aspnet_test | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/vulncheck-oss/go-exploit/aspnet" | ||
| ) | ||
|
|
||
| var pageState1 = `<!DOCTYPE html> | ||
| <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | ||
| <head><meta http-equiv="X-UA-Compatible" content="IE=9" /><meta http-equiv="Page-Enter" content="Alpha(opacity=100)" /><title> | ||
| Gladinet Cloud Cluster | ||
| </title> | ||
| <body style="overflow:hidden;"> | ||
| <form name="aspnetForm" method="post" action="./admindatabase.aspx" id="aspnetForm"> | ||
| <div> | ||
| <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> | ||
| <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> | ||
| <input type="hidden" name="__LASTFOCUS" id="__LASTFOCUS" value="" /> | ||
| <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYCAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBZmQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAQUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8BBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGRkhIVOv1laSf4FVfKCihTCvPyajtM=" /> | ||
| </div> | ||
| <div> | ||
| <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="C73717A7" /> | ||
| <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAAdexv6/qKqWdd7V9UzkVbKnzivrZbTfl5HxflMl0WEimkj+n3ntyqDMPWej+FjsRo61P6Uqwq7GZ15buFg7WHqF4VZwC+5O3u0TMTTYeToUrXDySQQEwxvyin+PIQ6Xt1JpqJ+bt/0dmbPhJrKioUwF82Mylv8B1bqOz6F0llEnG94eilk=" /> | ||
| </div> | ||
| </body> | ||
| </html>` | ||
|
|
||
| var pageState2 = `<!DOCTYPE html> | ||
| <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | ||
| <head><meta http-equiv="X-UA-Compatible" content="IE=9" /><meta http-equiv="Page-Enter" content="Alpha(opacity=100)" /><title> | ||
| Gladinet Cloud Cluster | ||
| </title> | ||
| <body style="overflow:hidden;"> | ||
| <form name="aspnetForm" method="post" action="./admindatabase.aspx" id="aspnetForm"> | ||
| <div> | ||
| <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> | ||
| <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> | ||
| <input type="hidden" name="__LASTFOCUS" id="__LASTFOCUS" value="" /> | ||
| <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYGAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBAgNkAj0PDxYCHgdWaXNpYmxlaGRkAkUPDxYCHwFnZGQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAgUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8CBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFIGN0bDAwJE1haW5Db250ZW50JFBTUUxDaGtTU0xNb2Rlt1OAugQHTFQSO9InFhq1a4zTB6w=" /> | ||
| </div> | ||
| <div> | ||
| <!-- contrived example removes <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="C73717A7" /> --> | ||
| <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAA1uUUuJru4fqcEgJkMrkl/VzivrZbTfl5HxflMl0WEimkj+n3ntyqDMPWej+FjsRo61P6Uqwq7GZ15buFg7WHqF4VZwC+5O3u0TMTTYeToUrXDySQQEwxvyin+PIQ6Xt1J+6SNjww5M+V+WUpWYV8cEoUTnLwGbguM3r6r03Xnunl50DFJPWsXTExtP5yQn7eqIN4VNCPK0IRBU8qYLZ2Qrlo7dTb8AdCT3V/XWpLNKSntkbVfk8X4Pe7mGcdZvwtNpqJ+bt/0dmbPhJrKioUwF+aS81hLoX5JwP8HKC0ur6/9jlQ8=" /> | ||
| </div> | ||
| </body> | ||
| </html> | ||
| ` | ||
|
|
||
| func TestState_Full(t *testing.T) { | ||
| state := aspnet.State{} | ||
| p := state.AsParams() | ||
| if len(p) != 0 { | ||
| t.Error("Parameters should not have state currently") | ||
| } | ||
|
|
||
| state.Update(pageState1) | ||
| p = state.AsParams() | ||
| if len(p) == 0 { | ||
| t.Error("Parameters should have state currently") | ||
| } | ||
| if len(p) != 5 { | ||
| t.Errorf("First state should only have 5 values: %d - %#v", len(p), p) | ||
| } | ||
|
|
||
| value, exists := p["__VIEWSTATE"] | ||
| if !exists { | ||
| t.Error("ViewState should be set on first request state update") | ||
| } | ||
| if value != `/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYCAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBZmQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAQUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8BBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGRkhIVOv1laSf4FVfKCihTCvPyajtM=` { | ||
| t.Error("ViewState on first update is unexpected") | ||
| } | ||
|
|
||
| value, exists = p["__LASTFOCUS"] | ||
| if !exists { | ||
| t.Error("LastFocus should not be nil") | ||
| } | ||
| if value != `` { | ||
| t.Error("LastFocus should be set but is an empty string") | ||
| } | ||
| if state.ViewStateGenerator == nil { | ||
| t.Errorf("ViewStateGenerator should not be nil on first request: %#v", state.ViewStateGenerator) | ||
| } | ||
|
|
||
| state.Update(pageState2) | ||
| p = state.AsParams() | ||
| if len(p) == 0 { | ||
| t.Error("Parameters should have state currently at state 2") | ||
| } | ||
| if len(p) != 4 { | ||
| t.Errorf("Second state should only have 4 values: %d - %#v", len(p), p) | ||
| } | ||
| if state.ViewStateGenerator != nil { | ||
| t.Errorf("ViewStateGenerator should be nil on second request: %#v", state.ViewStateGenerator) | ||
| } | ||
| if state.ViewState == nil { | ||
| t.Errorf("ViewState should be not be nil on second request: %#v", state.ViewStateGenerator) | ||
| } | ||
| if *state.ViewState != `/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYGAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBAgNkAj0PDxYCHgdWaXNpYmxlaGRkAkUPDxYCHwFnZGQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAgUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8CBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFIGN0bDAwJE1haW5Db250ZW50JFBTUUxDaGtTU0xNb2Rlt1OAugQHTFQSO9InFhq1a4zTB6w=` { | ||
| t.Error("ViewState on second update is unexpected") | ||
| } | ||
| } | ||
|
|
||
| func TestState_Merge(t *testing.T) { | ||
| state := aspnet.State{} | ||
| p := state.AsParams() | ||
| if len(p) != 0 { | ||
| t.Error("Parameters should not have state currently") | ||
| } | ||
|
|
||
| state.Update(pageState1) | ||
| p = state.AsParams() | ||
| if len(p) == 0 { | ||
| t.Error("Parameters should have state currently") | ||
| } | ||
| if len(p) != 5 { | ||
| t.Errorf("State should only have 5 values: %d - %#v", len(p), p) | ||
| } | ||
| v := map[string]string{ | ||
| "STUFF": "THINGS", | ||
| } | ||
| merged := state.MergeParams(v) | ||
| if len(merged) != 6 { | ||
| t.Errorf("State should have 6 values: %d - %#v", len(p), p) | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.