Skip to content

Commit 273dc59

Browse files
Merge branch 'main' of github.com:vulncheck-oss/go-exploit
2 parents d1ef910 + 9362a98 commit 273dc59

File tree

10 files changed

+1197
-5
lines changed

10 files changed

+1197
-5
lines changed

aspnet/aspnet.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// 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.
2+
//
3+
// The package can be used to facilitate chains of go-exploit requests to ASP.NET applications like so:
4+
//
5+
// state := aspnet.State{}
6+
// resp, body, ok := protocol.HTTPSendAndRecvWith("GET", conf.GenerateURL("/management/AdminDatabase.aspx"), "")
7+
// if !ok {
8+
// output.PrintError("Could not retrieve to the admin database endpoint")
9+
//
10+
// return false
11+
// }
12+
//
13+
// state.Update(body)
14+
//
15+
// // Now only the parameters that are required can be utilized and no special body parsing
16+
// // for __VIEWSTATE and friends is required.
17+
// p := state.MergeParams(map[string]string{
18+
// "__EVENTTARGET": "ctl00$MainContent$DatabaseType",
19+
// "ctl00%24MainContent%24DatabaseType": "psql",
20+
// })
21+
// params := protocol.CreateRequestParamsEncoded(p)
22+
// headers["Content-Type"] = "application/x-www-form-urlencoded"
23+
// resp, body, ok = protocol.HTTPSendAndRecvWithHeaders("POST", conf.GenerateURL("/management/AdminDatabase.aspx"), params, headers)
24+
// if !ok {
25+
// output.PrintError("Could not POST to the admin database endpoint")
26+
//
27+
// return false
28+
// }
29+
//
30+
// // Update the state from the previous POST response, this time we only want the states and have no content
31+
// state.Update(body)
32+
// params := protocol.CreateRequestParamsEncoded(state.AsParams())
33+
// resp, body, ok := protocol.HTTPSendAndRecvWithHeaders("POST", conf.GenerateURL("/management/AdminDatabase.aspx"), params, headers)
34+
// if !ok {
35+
// output.PrintError("Could not POST to the admin database endpoint")
36+
//
37+
// return false
38+
// }
39+
package aspnet
40+
41+
import (
42+
"maps"
43+
"strings"
44+
45+
"github.com/antchfx/htmlquery"
46+
)
47+
48+
// 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 struct 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.
49+
type State struct {
50+
ViewState *string
51+
ViewStateGenerator *string
52+
EventTarget *string
53+
EventValidation *string
54+
EventArgument *string
55+
LastFocus *string
56+
}
57+
58+
// xPathQuiet is similar to search.XPath, but does not trigger framework errors as these can be expected to be empty.
59+
func xPathQuiet(document, path string) (string, bool) {
60+
doc, err := htmlquery.Parse(strings.NewReader(document))
61+
if err != nil {
62+
return "", false
63+
}
64+
n := htmlquery.FindOne(doc, path)
65+
if n == nil {
66+
return "", false
67+
}
68+
69+
return htmlquery.InnerText(n), true
70+
}
71+
72+
// 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.
73+
func (state *State) AsParams() map[string]string {
74+
u := map[string]string{}
75+
if state.ViewState != nil {
76+
u["__VIEWSTATE"] = *state.ViewState
77+
}
78+
if state.ViewStateGenerator != nil {
79+
u["__VIEWSTATEGENERATOR"] = *state.ViewStateGenerator
80+
}
81+
if state.EventValidation != nil {
82+
u["__EVENTVALIDATION"] = *state.EventValidation
83+
}
84+
if state.EventArgument != nil {
85+
u["__EVENTARGUMENT"] = *state.EventArgument
86+
}
87+
if state.EventTarget != nil {
88+
u["__EVENTTARGET"] = *state.EventTarget
89+
}
90+
if state.LastFocus != nil {
91+
u["__LASTFOCUS"] = *state.LastFocus
92+
}
93+
94+
return u
95+
}
96+
97+
// 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.
98+
func (state *State) MergeParams(p map[string]string) map[string]string {
99+
params := state.AsParams()
100+
maps.Copy(params, p)
101+
102+
return params
103+
}
104+
105+
// 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.
106+
func (state *State) Update(body string) {
107+
v, hasMatch := xPathQuiet(body, `//input[@name="__VIEWSTATE"]/@value`)
108+
if hasMatch {
109+
state.ViewState = &v
110+
} else {
111+
state.ViewState = nil
112+
}
113+
vg, hasMatch := xPathQuiet(body, `//input[@name="__VIEWSTATEGENERATOR"]/@value`)
114+
if hasMatch {
115+
state.ViewStateGenerator = &vg
116+
} else {
117+
state.ViewStateGenerator = nil
118+
}
119+
ev, hasMatch := xPathQuiet(body, `//input[@name="__EVENTVALIDATION"]/@value`)
120+
if hasMatch {
121+
state.EventValidation = &ev
122+
} else {
123+
state.EventValidation = nil
124+
}
125+
et, hasMatch := xPathQuiet(body, `//input[@name="__EVENTTARGET"]/@value`)
126+
if hasMatch {
127+
state.EventTarget = &et
128+
} else {
129+
state.EventTarget = nil
130+
}
131+
ea, hasMatch := xPathQuiet(body, `//input[@name="__EVENTARGUMENT"]/@value`)
132+
if hasMatch {
133+
state.EventArgument = &ea
134+
} else {
135+
state.EventArgument = nil
136+
}
137+
lf, hasMatch := xPathQuiet(body, `//input[@name="__LASTFOCUS"]/@value`)
138+
if hasMatch {
139+
state.LastFocus = &lf
140+
} else {
141+
state.LastFocus = nil
142+
}
143+
}

aspnet/aspnet_test.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package aspnet_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/vulncheck-oss/go-exploit/aspnet"
7+
)
8+
9+
var pageState1 = `<!DOCTYPE html>
10+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
11+
<head><meta http-equiv="X-UA-Compatible" content="IE=9" /><meta http-equiv="Page-Enter" content="Alpha(opacity=100)" /><title>
12+
Gladinet Cloud Cluster
13+
</title>
14+
<body style="overflow:hidden;">
15+
<form name="aspnetForm" method="post" action="./admindatabase.aspx" id="aspnetForm">
16+
<div>
17+
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
18+
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
19+
<input type="hidden" name="__LASTFOCUS" id="__LASTFOCUS" value="" />
20+
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYCAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBZmQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAQUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8BBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGRkhIVOv1laSf4FVfKCihTCvPyajtM=" />
21+
</div>
22+
<div>
23+
<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="C73717A7" />
24+
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAAdexv6/qKqWdd7V9UzkVbKnzivrZbTfl5HxflMl0WEimkj+n3ntyqDMPWej+FjsRo61P6Uqwq7GZ15buFg7WHqF4VZwC+5O3u0TMTTYeToUrXDySQQEwxvyin+PIQ6Xt1JpqJ+bt/0dmbPhJrKioUwF82Mylv8B1bqOz6F0llEnG94eilk=" />
25+
</div>
26+
</body>
27+
</html>`
28+
29+
var pageState2 = `<!DOCTYPE html>
30+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
31+
<head><meta http-equiv="X-UA-Compatible" content="IE=9" /><meta http-equiv="Page-Enter" content="Alpha(opacity=100)" /><title>
32+
Gladinet Cloud Cluster
33+
</title>
34+
<body style="overflow:hidden;">
35+
<form name="aspnetForm" method="post" action="./admindatabase.aspx" id="aspnetForm">
36+
<div>
37+
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
38+
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
39+
<input type="hidden" name="__LASTFOCUS" id="__LASTFOCUS" value="" />
40+
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYGAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBAgNkAj0PDxYCHgdWaXNpYmxlaGRkAkUPDxYCHwFnZGQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAgUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8CBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFIGN0bDAwJE1haW5Db250ZW50JFBTUUxDaGtTU0xNb2Rlt1OAugQHTFQSO9InFhq1a4zTB6w=" />
41+
</div>
42+
<div>
43+
<!-- contrived example removes <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="C73717A7" /> -->
44+
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAA1uUUuJru4fqcEgJkMrkl/VzivrZbTfl5HxflMl0WEimkj+n3ntyqDMPWej+FjsRo61P6Uqwq7GZ15buFg7WHqF4VZwC+5O3u0TMTTYeToUrXDySQQEwxvyin+PIQ6Xt1J+6SNjww5M+V+WUpWYV8cEoUTnLwGbguM3r6r03Xnunl50DFJPWsXTExtP5yQn7eqIN4VNCPK0IRBU8qYLZ2Qrlo7dTb8AdCT3V/XWpLNKSntkbVfk8X4Pe7mGcdZvwtNpqJ+bt/0dmbPhJrKioUwF+aS81hLoX5JwP8HKC0ur6/9jlQ8=" />
45+
</div>
46+
</body>
47+
</html>
48+
`
49+
50+
func TestState_Full(t *testing.T) {
51+
state := aspnet.State{}
52+
p := state.AsParams()
53+
if len(p) != 0 {
54+
t.Error("Parameters should not have state currently")
55+
}
56+
57+
state.Update(pageState1)
58+
p = state.AsParams()
59+
if len(p) == 0 {
60+
t.Error("Parameters should have state currently")
61+
}
62+
if len(p) != 6 {
63+
t.Errorf("First state should only have 6 values: %d - %#v", len(p), p)
64+
}
65+
66+
value, exists := p["__VIEWSTATE"]
67+
if !exists {
68+
t.Error("ViewState should be set on first request state update")
69+
}
70+
if value != `/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYCAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBZmQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAQUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8BBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGRkhIVOv1laSf4FVfKCihTCvPyajtM=` {
71+
t.Error("ViewState on first update is unexpected")
72+
}
73+
74+
value, exists = p["__LASTFOCUS"]
75+
if !exists {
76+
t.Error("LastFocus should not be nil")
77+
}
78+
if value != `` {
79+
t.Error("LastFocus should be set but is an empty string")
80+
}
81+
if state.ViewStateGenerator == nil {
82+
t.Errorf("ViewStateGenerator should not be nil on first request: %#v", state.ViewStateGenerator)
83+
}
84+
85+
state.Update(pageState2)
86+
p = state.AsParams()
87+
if len(p) == 0 {
88+
t.Error("Parameters should have state currently at state 2")
89+
}
90+
if len(p) != 5 {
91+
t.Errorf("Second state should only have 5 values: %d - %#v", len(p), p)
92+
}
93+
if state.ViewStateGenerator != nil {
94+
t.Errorf("ViewStateGenerator should be nil on second request: %#v", state.ViewStateGenerator)
95+
}
96+
if state.ViewState == nil {
97+
t.Errorf("ViewState should be not be nil on second request: %#v", state.ViewStateGenerator)
98+
}
99+
if *state.ViewState != `/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYGAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBAgNkAj0PDxYCHgdWaXNpYmxlaGRkAkUPDxYCHwFnZGQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAgUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8CBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFIGN0bDAwJE1haW5Db250ZW50JFBTUUxDaGtTU0xNb2Rlt1OAugQHTFQSO9InFhq1a4zTB6w=` {
100+
t.Error("ViewState on second update is unexpected")
101+
}
102+
}
103+
104+
func TestState_Each(t *testing.T) {
105+
state := aspnet.State{}
106+
p := state.AsParams()
107+
if len(p) != 0 {
108+
t.Error("Parameters should not have state currently")
109+
}
110+
111+
state.Update(pageState1)
112+
p = state.AsParams()
113+
if len(p) == 0 {
114+
t.Error("Parameters should have state currently")
115+
}
116+
if len(p) != 6 {
117+
t.Errorf("First state should only have 6 values: %d - %#v", len(p), p)
118+
}
119+
120+
value, exists := p["__VIEWSTATE"]
121+
if !exists {
122+
t.Error("ViewState should be set on first request state update")
123+
}
124+
if value != `/wEPDwULLTE4OTcxMDA5NzIPZBYCZg9kFgQCAw8WAh4EVGV4dGVkAgUPZBYIAgYPZBYCAjsPEGQPFgRmAgECAgIDFgQQBRREZWZhdWx0IC0gYWxsIGluIG9uZQUHZGVmYXVsdGcQBQZNeSBTcWwFBW15c3FsZxAFClNRTCBTZXJ2ZXIFA3NxbGcQBQpQb3N0Z3JlU1FMBQRwc3FsZxYBZmQCCA8PFgIeC05hdmlnYXRlVXJsBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL2NvbnRhY3QuaHRtZGQCCQ8PFgIfAQUjaHR0cDovL3d3dy5nbGFkaW5ldC5jb20vcC90ZXJtcy5odG1kZAIKDw8WAh8BBSVodHRwOi8vd3d3LmdsYWRpbmV0LmNvbS9wL3ByaXZhY3kuaHRtZGRkhIVOv1laSf4FVfKCihTCvPyajtM=` {
125+
t.Error("ViewState on first update is unexpected")
126+
}
127+
128+
value, exists = p["__LASTFOCUS"]
129+
if !exists {
130+
t.Error("LastFocus should not be nil")
131+
}
132+
if value != `` {
133+
t.Error("LastFocus should be set but is an empty string")
134+
}
135+
value, exists = p["__VIEWSTATEGENERATOR"]
136+
if !exists {
137+
t.Error("ViewStateGenerator should not be nil")
138+
}
139+
if value != `C73717A7` {
140+
t.Error("ViewStateGenerator on first update is unexpected")
141+
}
142+
value, exists = p["__EVENTVALIDATION"]
143+
if !exists {
144+
t.Error("EventValidation should not be nil")
145+
}
146+
if value != `/wEdAAdexv6/qKqWdd7V9UzkVbKnzivrZbTfl5HxflMl0WEimkj+n3ntyqDMPWej+FjsRo61P6Uqwq7GZ15buFg7WHqF4VZwC+5O3u0TMTTYeToUrXDySQQEwxvyin+PIQ6Xt1JpqJ+bt/0dmbPhJrKioUwF82Mylv8B1bqOz6F0llEnG94eilk=` {
147+
t.Error("EventValidation on first update is unexpected")
148+
}
149+
if state.EventArgument == nil {
150+
t.Errorf("EventArgument should not be nil on second request: %#v", state.EventArgument)
151+
}
152+
if *state.EventArgument != "" {
153+
t.Errorf("EventArgument should be empty string on second request: %#v", state.EventArgument)
154+
}
155+
if state.EventTarget == nil {
156+
t.Errorf("EventTarget should not be nil on second request: %#v", state.EventTarget)
157+
}
158+
if *state.EventTarget != "" {
159+
t.Errorf("EventTarget should be empty string on second request: %#v", state.EventTarget)
160+
}
161+
}
162+
163+
func TestState_Merge(t *testing.T) {
164+
state := aspnet.State{}
165+
p := state.AsParams()
166+
if len(p) != 0 {
167+
t.Error("Parameters should not have state currently")
168+
}
169+
170+
state.Update(pageState1)
171+
p = state.AsParams()
172+
if len(p) == 0 {
173+
t.Error("Parameters should have state currently")
174+
}
175+
if len(p) != 6 {
176+
t.Errorf("State should only have 6 values: %d - %#v", len(p), p)
177+
}
178+
v := map[string]string{
179+
"STUFF": "THINGS",
180+
}
181+
merged := state.MergeParams(v)
182+
if len(merged) != 7 {
183+
t.Errorf("State should have 7 values: %d - %#v", len(p), p)
184+
}
185+
}

c2/channel/channel.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (c *Channel) AddSession(conn *net.Conn, addr string) bool {
8080
return true
8181
}
8282

83-
// Updates the LastSeen value for provided connection to the provided time
83+
// Updates the LastSeen value for provided connection to the provided time.
8484
func (c *Channel) UpdateLastSeenByConn(conn net.Conn, timeStamp time.Time) bool {
8585
id, ok := c.GetSessionIDByConn(conn)
8686
if !ok {
@@ -100,7 +100,7 @@ func (c *Channel) UpdateLastSeenByConn(conn net.Conn, timeStamp time.Time) bool
100100
return true
101101
}
102102

103-
// Returns the session ID that contains a given connection
103+
// Returns the session ID that contains a given connection.
104104
func (c *Channel) GetSessionIDByConn(conn net.Conn) (string, bool) {
105105
if len(c.Sessions) == 0 {
106106
output.PrintFrameworkDebug("No sessions exist")
@@ -119,7 +119,6 @@ func (c *Channel) GetSessionIDByConn(conn net.Conn) (string, bool) {
119119
return "", false
120120
}
121121

122-
123122
// RemoveSession removes a specific session ID and if a connection exists, closes it.
124123
func (c *Channel) RemoveSession(id string) bool {
125124
if len(c.Sessions) == 0 {

0 commit comments

Comments
 (0)