Skip to content

Commit

Permalink
Bug fixes for improper handling of the server object to populate base…
Browse files Browse the repository at this point in the history
… paths. Also added additional paths to the brute command.
  • Loading branch information
twest-bf committed Jul 3, 2024
1 parent 75da022 commit 91c8ffd
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 21 deletions.
11 changes: 6 additions & 5 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@ archives:
format: zip

changelog:
disable: false
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
groups:
- title: "New Features"
regexp: '^(Add|add)(s|ed|ing)?'
- title: Features
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: "Bug Fixes"
regexp: '^(Fix|fix)(es|ed|ing)?'
- title: "Bug fixes"
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: "Others"
- title: Others
order: 999
91 changes: 85 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,101 @@ $ go install github.com/BishopFox/sj@latest

> Use the `automate` command to send a series of requests to each defined endpoint and analyze the status code of each response.
![Automate Command](img/sj-automate.gif)
```bash
$ sj automate -u https://petstore.swagger.io/v2/swagger.json -q

INFO[0000] Gathering API details.

Title: Swagger Petstore
Description: This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.

INFO[0000] Available authentication mechanisms:
- api_key
- petstore_auth

INFO[0000] Endpoint accessible! Method=GET Status=200 Target="https://petstore.swagger.io/v2/pet/findByTags?tags=1"
INFO[0000] Endpoint accessible! Method=POST Status=200 Target="https://petstore.swagger.io/v2/store/order"
WARN[0000] Manual testing may be required. Method=POST Status=500 Target="https://petstore.swagger.io/v2/user/createWithList"
INFO[0000] Endpoint accessible! Method=POST Status=200 Target="https://petstore.swagger.io/v2/user"
ERRO[0000] User not found Method=GET Status=404 Target="https://petstore.swagger.io/v2/user/test"
WARN[0000] Manual testing may be required. Method=PUT Status=415 Target="https://petstore.swagger.io/v2/user/test"
INFO[0000] Endpoint accessible! Method=GET Status=200 Target="https://petstore.swagger.io/v2/user/login?password=test&username=test"
WARN[0000] Manual testing may be required. Method=POST Status=500 Target="https://petstore.swagger.io/v2/user/createWithArray"
INFO[0000] Endpoint accessible! Method=GET Status=200 Target="https://petstore.swagger.io/v2/user/logout"
WARN[0000] Manual testing may be required. Method=POST Status=415 Target="https://petstore.swagger.io/v2/pet/test"
ERRO[0000] Pet not found Method=GET Status=404 Target="https://petstore.swagger.io/v2/pet/test"
WARN[0000] Manual testing may be required. Method=POST Status=415 Target="https://petstore.swagger.io/v2/pet/test/uploadImage"
ERRO[0000] Order not found Method=GET Status=404 Target="https://petstore.swagger.io/v2/store/order/test"
INFO[0000] Endpoint accessible! Method=GET Status=200 Target="https://petstore.swagger.io/v2/store/inventory"
INFO[0000] Endpoint accessible! Method=POST Status=200 Target="https://petstore.swagger.io/v2/pet"
WARN[0000] Manual testing may be required. Method=PUT Status=415 Target="https://petstore.swagger.io/v2/pet"
INFO[0001] Endpoint accessible! Method=GET Status=200 Target="https://petstore.swagger.io/v2/pet/findByStatus?status=1"
```
> Use the `prepare` command to prepare a list of commands for manual testing. Currently supports both `curl` and `sqlmap`. You will likely have to modify these slightly.
![Prepare Command](img/sj-prepare.gif)
```bash
$ sj prepare -u https://petstore.swagger.io/v2/swagger.json -q

INFO[0000] Gathering API details.

Title: Swagger Petstore
Description: This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.

INFO[0000] Available authentication mechanisms:
- api_key
- petstore_auth

curl -sk -X PUT 'https://petstore.swagger.io/v2/user/test' -d '{"test":"test"}'
curl -sk -X GET 'https://petstore.swagger.io/v2/user/test'
curl -sk -X GET 'https://petstore.swagger.io/v2/pet/test'
curl -sk -X POST 'https://petstore.swagger.io/v2/pet/test' -d '{"test":"test"}'
curl -sk -X POST 'https://petstore.swagger.io/v2/store/order' -d '{"test":"test"}'
curl -sk -X POST 'https://petstore.swagger.io/v2/pet' -d '{"test":"test"}'
curl -sk -X PUT 'https://petstore.swagger.io/v2/pet' -d '{"test":"test"}'
curl -sk -X GET 'https://petstore.swagger.io/v2/store/order/test'
curl -sk -X GET 'https://petstore.swagger.io/v2/pet/findByTags?tags=1'
curl -sk -X POST 'https://petstore.swagger.io/v2/user/createWithList' -d '{"test":"test"}'
curl -sk -X POST 'https://petstore.swagger.io/v2/user' -d '{"test":"test"}'
curl -sk -X GET 'https://petstore.swagger.io/v2/user/logout'
curl -sk -X GET 'https://petstore.swagger.io/v2/user/login?password=test&username=test'
curl -sk -X POST 'https://petstore.swagger.io/v2/pet/test/uploadImage' -d '{"test":"test"}'
curl -sk -X GET 'https://petstore.swagger.io/v2/pet/findByStatus?status=1'
curl -sk -X GET 'https://petstore.swagger.io/v2/store/inventory'
curl -sk -X POST 'https://petstore.swagger.io/v2/user/createWithArray' -d '{"test":"test"}'
```
> Use the `endpoints` command to generate a list of raw endpoints from the provided definition file.
![Endpoints Command](img/sj-endpoints.gif)
```bash
$ sj endpoints -u https://petstore.swagger.io/v2/swagger.json

INFO[0000] Gathering endpoints.

/v2/pet
/v2/pet/findByStatus
/v2/pet/findByTags
/v2/pet/{petId}
/v2/pet/{petId}/uploadImage
/v2/store/inventory
/v2/store/order
/v2/store/order/{orderId}
/v2/user
/v2/user/createWithArray
/v2/user/createWithList
/v2/user/login
/v2/user/logout
/v2/user/{username}
```
> Use the `brute` command to send a series of requests in an attempt to find a definition file on the target.
```bash
$ sj brute -u https://<TARGET>
INFO[0000] Sending 2045 requests. This could take a while...
INFO[0111] Found operation definitions embedded in JavaScript file at https://<TARGET>/example.js
$ sj brute -u https://petstore.swagger.io/v2/swagger.json
INFO[0000] Sending 2045 requests. This could take a while...
Request: 343
INFO[0015] Definition file found: https://petstore.swagger.io/v2/swagger
{"...SNIP..."}
```
## Help
Expand Down
6 changes: 4 additions & 2 deletions cmd/brute.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var (
"/docs/v2",
"/docs/v3",
"/public",
"/redoc",
}
jsonEndpoints = []string{
"",
Expand Down Expand Up @@ -108,7 +109,8 @@ func findDefinitionFile(urls []string, client http.Client) (bool, *openapi3.T) {
if bodyBytes != nil {
checkSpec := UnmarshalSpec(bodyBytes)
if (strings.HasPrefix(checkSpec.OpenAPI, "2") || strings.HasPrefix(checkSpec.OpenAPI, "3")) && checkSpec.Paths != nil {
log.Infof("\nDefinition file found: %s\n", url)
fmt.Println("")
log.Infof("Definition file found: %s\n", url)
return true, checkSpec
}
}
Expand All @@ -127,7 +129,7 @@ func findDefinitionFile(urls []string, client http.Client) (bool, *openapi3.T) {
}
}
}
if i+1 == len(urls) {
if i == len(urls) {
fmt.Printf("\033[2K\r%s%d\n", "Request: ", i+1)
} else {
fmt.Printf("\033[2K\r%s%d", "Request: ", i+1)
Expand Down
6 changes: 3 additions & 3 deletions cmd/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func writeLog(sc int, target, method string, errorMsg string) {
} else if sc == 301 || sc == 302 {
logRedirect(sc, target, method)
} else if sc == 0 {
logSkipped(sc, target, method)
logBad(sc, target, method)
} else if sc == 404 {
logNotFound(sc, target, method, errorMsg)
} else if sc == 1 {
Expand Down Expand Up @@ -70,12 +70,12 @@ func logRedirect(status int, target, method string) {
}).Error("Redirect detected. This likely requires authentication.")
}

func logSkipped(status int, target, method string) {
func logBad(status int, target, method string) {
log.WithFields(log.Fields{
"Status": "N/A",
"Target": target,
"Method": method,
}).Warn("Request skipped (dangerous keyword found).")
}).Warn("Bad request (could not reach the target).")
}

func logUnauth(status int, target, method, errorMsg string) {
Expand Down
1 change: 1 addition & 0 deletions cmd/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func MakeRequest(client http.Client, method, target string, timeout int64, reqDa
bodyBytes, _ := io.ReadAll(resp.Body)
bodyString := string(bodyBytes)
requestStatus = resp.StatusCode
fmt.Println(resp.Status)

return bodyBytes, bodyString, requestStatus
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ $ sj brute -u https://petstore.swagger.io`,
log.Error("Command not specified. See the --help flag for usage.")
}
},
Version: "1.4.2",
Version: "1.4.3",
}

func Execute() {
Expand Down
17 changes: 13 additions & 4 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ func GenerateRequests(bodyBytes []byte, client http.Client) []string {
}

func GetBasePath(servers openapi3.Servers, host string) (bp string) {
if strings.Contains(host, ":") {
hostPortStart := strings.Index(host, ":")
host = host[0:hostPortStart]
}
if basePath == "" {
if servers != nil {
s1, _ := url.Parse(servers[0].URL)
Expand All @@ -338,12 +342,17 @@ func GetBasePath(servers openapi3.Servers, host string) (bp string) {
}
if servers[0].URL == "/" {
basePath = "/"
} else {
} else if strings.Contains(servers[0].URL, "http") && !strings.Contains(servers[0].URL, host) { // Check to see if the server object being used for the base path contains a different host than the target
basePath = servers[0].URL
if strings.Contains(host, ":") {
hostPortStart := strings.Index(host, ":")
host = host[0:hostPortStart]
basePath = strings.ReplaceAll(basePath, "http://", "")
basePath = strings.ReplaceAll(basePath, "https://", "")
indexSubdomain := strings.Index(basePath, "/")
basePath = basePath[indexSubdomain:]
if !strings.HasSuffix(basePath, "/") {
basePath = basePath + "/"
}
} else {
basePath = servers[0].URL
if strings.Contains(basePath, host) || strings.Contains(basePath, "http") {
basePath = strings.ReplaceAll(basePath, host, "")
basePath = strings.ReplaceAll(basePath, "http://", "")
Expand Down

0 comments on commit 91c8ffd

Please sign in to comment.