Compare commits

..
3 Commits
9 changed files with 69 additions and 38 deletions

No files matched your search

+4 -4
View File
@@ -32,11 +32,11 @@ changelog:
- "^docs:"
- "^test:"
groups:
- title: New Features
regexp: '(?i)^adds*'
- title: "New Features"
regexp: '^(Add|add)(s|ed|ing)?'
order: 0
- title: "Bug Fixes"
regexp: '(?i)^fix*'
regexp: '^(Fix|fix)(es|ed|ing)?'
order: 1
- title: Others
- title: "Others"
order: 999
+1 -1
View File
@@ -32,7 +32,7 @@ $ go install github.com/BishopFox/sj@latest
![Automate Command](img/sj-automate.gif)
> Use the `prepare` command to prepare a list of curl commands for manual testing. You will likely have to modify these slightly.
> 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)
+2 -2
View File
@@ -37,7 +37,7 @@ responds in an abnormal way, manual testing should be conducted (prepare manual
}
if swaggerURL != "" {
bodyBytes, _, _ = MakeRequest(client, "GET", swaggerURL, timeout, nil, "automate")
bodyBytes, _, _ = MakeRequest(client, "GET", swaggerURL, timeout, nil)
} else {
specFile, err := os.Open(localFile)
if err != nil {
@@ -46,7 +46,7 @@ responds in an abnormal way, manual testing should be conducted (prepare manual
bodyBytes, _ = io.ReadAll(specFile)
}
GenerateRequests(bodyBytes, client, "automate")
GenerateRequests(bodyBytes, client)
},
}
+2 -2
View File
@@ -104,7 +104,7 @@ func findDefinitionFile(urls []string, client http.Client) (bool, *openapi3.T) {
for i, url := range urls {
ct := CheckContentType(client, url)
if strings.Contains(ct, "application/json") {
bodyBytes, _, _ := MakeRequest(client, "GET", url, timeout, nil, "brute")
bodyBytes, _, _ := MakeRequest(client, "GET", url, timeout, nil)
if bodyBytes != nil {
checkSpec := UnmarshalSpec(bodyBytes)
if (strings.HasPrefix(checkSpec.OpenAPI, "2") || strings.HasPrefix(checkSpec.OpenAPI, "3")) && checkSpec.Paths != nil {
@@ -113,7 +113,7 @@ func findDefinitionFile(urls []string, client http.Client) (bool, *openapi3.T) {
}
}
} else if strings.Contains(ct, "application/javascript") {
bodyBytes, bodyString, _ := MakeRequest(client, "GET", url, timeout, nil, "brute")
bodyBytes, bodyString, _ := MakeRequest(client, "GET", url, timeout, nil)
if bodyBytes != nil {
regexPattern := regexp.MustCompile(`(?s)let\s+(\w+)\s*=\s*({.*?});`)
matches := regexPattern.FindAllStringSubmatch(bodyString, -1)
+3 -3
View File
@@ -25,15 +25,15 @@ This list contains the raw endpoints (parameter values will not be appended or m
log.Infof("Gathering endpoints.\n\n")
if swaggerURL != "" {
bodyBytes, _, _ := MakeRequest(client, "GET", swaggerURL, timeout, nil, "endpoints")
paths = GenerateRequests(bodyBytes, client, "endpoints")
bodyBytes, _, _ := MakeRequest(client, "GET", swaggerURL, timeout, nil)
paths = GenerateRequests(bodyBytes, client)
} else {
specFile, err := os.Open(localFile)
if err != nil {
log.Fatal("Error opening file:", err)
}
bodyBytes, _ = io.ReadAll(specFile)
paths = GenerateRequests(bodyBytes, client, "endpoints")
paths = GenerateRequests(bodyBytes, client)
}
unique.Sort(unique.StringSlice{&paths})
for _, v := range paths {
+9 -4
View File
@@ -12,7 +12,7 @@ import (
var prepareCmd = &cobra.Command{
Use: "prepare",
Short: "Prepares a set of commands for manual testing of each endpoint.",
Long: `The prepare command prepares a set of curl commands for manual testing of each endpoint.
Long: `The prepare command prepares a set of commands for manual testing of each endpoint.
This enables you to test specific API functions for common vulnerabilities or misconfigurations.`,
Run: func(cmd *cobra.Command, args []string) {
client := CheckAndConfigureProxy()
@@ -20,8 +20,8 @@ This enables you to test specific API functions for common vulnerabilities or mi
fmt.Printf("\n")
log.Infof("Gathering API details.\n\n")
if swaggerURL != "" {
bodyBytes, _, _ := MakeRequest(client, "GET", swaggerURL, timeout, nil, "prepare")
GenerateRequests(bodyBytes, client, "prepare")
bodyBytes, _, _ := MakeRequest(client, "GET", swaggerURL, timeout, nil)
GenerateRequests(bodyBytes, client)
} else {
specFile, err := os.Open(localFile)
if err != nil {
@@ -29,7 +29,12 @@ This enables you to test specific API functions for common vulnerabilities or mi
}
specBytes, _ := io.ReadAll(specFile)
GenerateRequests(specBytes, client, "prepare")
GenerateRequests(specBytes, client)
}
},
}
var prepareFor string
func init() {
prepareCmd.PersistentFlags().StringVarP(&prepareFor, "external-tool", "e", "curl", "The external tool to prepare commands for. Generates syntax for 'curl' by default.")
}
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"math/rand"
"net/http"
"net/url"
"os"
"strings"
"time"
@@ -51,7 +52,7 @@ var (
userChoice string
)
func MakeRequest(client http.Client, method, target string, timeout int64, reqData io.Reader, command string) ([]byte, string, int) {
func MakeRequest(client http.Client, method, target string, timeout int64, reqData io.Reader) ([]byte, string, int) {
if quiet {
avoidDangerousRequests = "y"
}
@@ -60,7 +61,7 @@ func MakeRequest(client http.Client, method, target string, timeout int64, reqDa
u, _ := url.Parse(target)
endpoint := u.RawPath + "?" + u.RawQuery
for _, v := range dangerousStrings {
if command == "automate" && strings.Contains(endpoint, v) && !strings.Contains(strings.Join(safeWords, ","), v) {
if os.Args[1] == "automate" && strings.Contains(endpoint, v) && !strings.Contains(strings.Join(safeWords, ","), v) {
userChoice = ""
if avoidDangerousRequests == "y" {
return nil, "", 0
+2 -2
View File
@@ -32,7 +32,7 @@ Example usage:
Perform a quick check of endpoints which require authentication:
$ sj automate -u https://petstore.swagger.io/v2/swagger.json
Generate a list of curl commands to use for manual testing:
Generate a list of commands to use for manual testing:
$ sj prepare -u https://petstore.swagger.io/v2/swagger.json
Generate a list of raw API routes for use with custom scripts:
@@ -46,7 +46,7 @@ $ sj brute -u https://petstore.swagger.io`,
log.Error("Command not specified. See the --help flag for usage.")
}
},
Version: "1.3.6",
Version: "1.4.2",
}
func Execute() {
+43 -18
View File
@@ -93,7 +93,7 @@ func ExtractSpecFromJS(bodyBytes []byte) []byte {
return bodyBytes
}
func GenerateRequests(bodyBytes []byte, client http.Client, command string) []string {
func GenerateRequests(bodyBytes []byte, client http.Client) []string {
var apiKey string
var apiKeyName string
var apiInQuery bool = false
@@ -107,13 +107,13 @@ func GenerateRequests(bodyBytes []byte, client http.Client, command string) []st
// BuildObjectsFromSchemaDefinitions(*newDoc) TODO
if command != "endpoints" {
if os.Args[1] != "endpoints" {
// Prints Title/Description values if they exist
PrintSpecInfo(*newDoc.Info)
apiInQuery, apiKey, apiKeyName = CheckSecDefs(*newDoc)
}
if command == "automate" && outfile != "" {
if os.Args[1] == "automate" && outfile != "" {
WriteJSONFile(`"results":[`, false)
}
@@ -204,8 +204,8 @@ func GenerateRequests(bodyBytes []byte, client http.Client, command string) []st
u.Path = u.Path + newPath
u.RawQuery = query.Encode()
if command == "automate" {
_, _, sc := MakeRequest(client, method, u.String(), timeout, bytes.NewReader([]byte(bodyData)), command)
if os.Args[1] == "automate" {
_, _, sc := MakeRequest(client, method, u.String(), timeout, bytes.NewReader([]byte(bodyData)))
if sc == 200 {
accessibleEndpointFound = true
accessibleEndpoints = append(accessibleEndpoints, u.String())
@@ -238,25 +238,46 @@ func GenerateRequests(bodyBytes []byte, client http.Client, command string) []st
writeLog(sc, u.String(), method, errorDescriptions[fmt.Sprint(sc)])
}
}
} else if command == "prepare" {
if bodyData == nil {
if len(Headers) == 0 {
fmt.Printf("curl -sk -X %s '%s'\n", method, u.String())
} else if os.Args[1] == "prepare" {
if strings.ToLower(prepareFor) == "curl" {
if bodyData == nil {
if len(Headers) == 0 {
fmt.Printf("curl -sk -X %s '%s'\n", method, u.String())
} else {
fmt.Printf("curl -sk -X %s '%s' -H '%s'\n", method, u.String(), strings.Join(Headers, "' -H '"))
}
} else {
fmt.Printf("curl -sk -X %s '%s' -H '%s'\n", method, u.String(), strings.Join(Headers, "' -H '"))
if len(Headers) == 0 {
fmt.Printf("curl -sk -X %s '%s' -d '%s'\n", method, u.String(), bodyData)
} else {
fmt.Printf("curl -sk -X %s '%s' -d '%s' -H '%s'\n", method, u.String(), bodyData, strings.Join(Headers, "' -H '"))
}
}
} else if strings.ToLower(prepareFor) == "sqlmap" {
if bodyData == nil {
if len(Headers) == 0 {
fmt.Printf("sqlmap -u %s\n", u.String())
} else {
fmt.Printf("sqlmap -u %s -H '%s'\n", u.String(), strings.Join(Headers, "' -H '"))
}
} else {
if len(Headers) == 0 {
fmt.Printf("sqlmap -u %s --data='%s'\n", u.String(), bodyData)
} else {
fmt.Printf("sqlmap -u %s --data='%s' -H '%s'\n", u.String(), bodyData, strings.Join(Headers, "' -H '"))
}
}
} else if strings.ToLower(prepareFor) == "ffuf" {
// TODO
} else {
if len(Headers) == 0 {
fmt.Printf("curl -sk -X %s '%s' -d '%s'\n", method, u.String(), bodyData)
} else {
fmt.Printf("curl -sk -X %s '%s' -d '%s' -H '%s'\n", method, u.String(), bodyData, strings.Join(Headers, "' -H '"))
}
log.Fatal("External tool not supported. Only 'curl' and 'sqlmap' are supported options for the '-e' flag at this time.")
}
} else if command == "endpoints" {
} else if os.Args[1] == "endpoints" {
for k := range newDoc.Paths {
if !pathMap[k] {
paths = append(paths, k)
paths = append(paths, basePathResult+k)
pathMap[k] = true
}
}
@@ -266,7 +287,7 @@ func GenerateRequests(bodyBytes []byte, client http.Client, command string) []st
}
}
if command == "automate" {
if os.Args[1] == "automate" {
if outfile == "" && getAccessibleEndpoints && strings.ToLower(outputFormat) == "console" {
var isDuplicateEndpoint bool
var printedEndpoints []string
@@ -319,6 +340,10 @@ func GetBasePath(servers openapi3.Servers, host string) (bp string) {
basePath = "/"
} else {
basePath = servers[0].URL
if strings.Contains(host, ":") {
hostPortStart := strings.Index(host, ":")
host = host[0:hostPortStart]
}
if strings.Contains(basePath, host) || strings.Contains(basePath, "http") {
basePath = strings.ReplaceAll(basePath, host, "")
basePath = strings.ReplaceAll(basePath, "http://", "")