Skip to content

Commit

Permalink
handle regex based payloads
Browse files Browse the repository at this point in the history
add os command injection test
  • Loading branch information
dmdhrumilmistry committed Aug 25, 2024
1 parent 0e04052 commit d700b6b
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/cmd/offat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func main() {
RunUnrestrictedHttpMethodTest: true,
RunBasicSQLiTest: true,
RunBasicSSRFTest: true,
RunOsCommandInjectionTest: true,

// SSRF Test
SsrfUrl: *config.SsrfUrl,
Expand Down
16 changes: 8 additions & 8 deletions src/pkg/tgen/basicSqliTest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import (
func BasicSqliTest(baseUrl string, docParams []*parser.DocHttpParams, queryParams map[string]string, headers map[string]string, injectionConfig InjectionConfig) []*ApiTest {
testName := "Basic SQLI Test"
vulnResponseCodes := []int{500}
immuneResponseCodes := []int{}

// TODO: implement injection in both keys and values
payloads := []string{
"' OR 1=1 ;--",
"' UNION SELECT 1,2,3 -- -",
"' OR '1'='1--",
"' AND (SELECT * FROM (SELECT(SLEEP(5)))abc)",
"' AND SLEEP(5) --",
payloads := []Payload{
{InjText: "' OR 1=1 ;--", VulnerableResponseCodes: vulnResponseCodes},
{InjText: "' UNION SELECT 1,2,3 -- -", VulnerableResponseCodes: vulnResponseCodes},
{InjText: "' OR '1'='1--", VulnerableResponseCodes: vulnResponseCodes},
{InjText: "' AND (SELECT * FROM (SELECT(SLEEP(5)))abc)", VulnerableResponseCodes: vulnResponseCodes},
{InjText: "' AND SLEEP(5) --", VulnerableResponseCodes: vulnResponseCodes},
}

injectionConfig.Payloads = payloads

tests := injectParamIntoApiTest(baseUrl, docParams, queryParams, headers, testName, vulnResponseCodes, immuneResponseCodes, injectionConfig)
tests := injectParamIntoApiTest(baseUrl, docParams, queryParams, headers, testName, injectionConfig)

return tests
}
8 changes: 5 additions & 3 deletions src/pkg/tgen/basicSsrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import "github.com/OWASP/OFFAT/src/pkg/parser"
func BasicSsrfTest(ssrfUrl, baseUrl string, docParams []*parser.DocHttpParams, queryParams map[string]string, headers map[string]string, injectionConfig InjectionConfig) []*ApiTest {
testName := "Basic SSRF Test"
vulnResponseCodes := []int{500}
immuneResponseCodes := []int{}
payloads := []string{ssrfUrl}

payloads := []Payload{
{InjText: ssrfUrl, VulnerableResponseCodes: vulnResponseCodes},
}

injectionConfig.Payloads = payloads

tests := injectParamIntoApiTest(baseUrl, docParams, queryParams, headers, testName, vulnResponseCodes, immuneResponseCodes, injectionConfig)
tests := injectParamIntoApiTest(baseUrl, docParams, queryParams, headers, testName, injectionConfig)

return tests
}
22 changes: 22 additions & 0 deletions src/pkg/tgen/osCommandInjection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package tgen

import (
"github.com/OWASP/OFFAT/src/pkg/parser"
)

func BasicOsCommandInjectionTest(baseUrl string, docParams []*parser.DocHttpParams, queryParams map[string]string, headers map[string]string, injectionConfig InjectionConfig) []*ApiTest {
testName := "Basic OS Command Injection Test"

// TODO: implement injection in both keys and values
payloads := []Payload{
{InjText: "cat /etc/passwd", Regex: "root:.*"},
{InjText: "cat /etc/shadow", Regex: "root:.*"},
{InjText: "ls -la", Regex: "total\\s\\d+"},
}

injectionConfig.Payloads = payloads

tests := injectParamIntoApiTest(baseUrl, docParams, queryParams, headers, testName, injectionConfig)

return tests
}
15 changes: 8 additions & 7 deletions src/pkg/tgen/payloadInjection.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func injectParamInParam(params *[]parser.Param, payload string) {
}

// generates Api tests by injecting payloads in values
func injectParamIntoApiTest(url string, docParams []*parser.DocHttpParams, queryParams map[string]string, headers map[string]string, testName string, vulnResponseCodes, immuneResponseCodes []int, injectionConfig InjectionConfig) []*ApiTest {
func injectParamIntoApiTest(url string, docParams []*parser.DocHttpParams, queryParams map[string]string, headers map[string]string, testName string, injectionConfig InjectionConfig) []*ApiTest {
var tests []*ApiTest
// TODO: only inject payloads if any payload is accepted by the endpoint, else ignore injection
// as this will reduce number of tests generated and increase efficiency
Expand All @@ -40,16 +40,16 @@ func injectParamIntoApiTest(url string, docParams []*parser.DocHttpParams, query
for _, docParam := range docParams {
// inject payloads into string before converting it to map[string]string
if injectionConfig.InBody {
injectParamInParam(&(docParam.BodyParams), payload)
injectParamInParam(&(docParam.BodyParams), payload.InjText)
}
if injectionConfig.InQuery {
injectParamInParam(&(docParam.QueryParams), payload)
injectParamInParam(&(docParam.QueryParams), payload.InjText)
}
if injectionConfig.InCookie {
injectParamInParam(&(docParam.CookieParams), payload)
injectParamInParam(&(docParam.CookieParams), payload.InjText)
}
if injectionConfig.InHeader {
injectParamInParam(&(docParam.HeaderParams), payload)
injectParamInParam(&(docParam.HeaderParams), payload.InjText)
}

// parse maps
Expand All @@ -75,8 +75,9 @@ func injectParamIntoApiTest(url string, docParams []*parser.DocHttpParams, query
Request: request,
Path: docParam.Path,
PathWithParams: pathWithParams,
VulnerableResponseCodes: vulnResponseCodes,
ImmuneResponseCodes: immuneResponseCodes,
VulnerableResponseCodes: payload.VulnerableResponseCodes,
ImmuneResponseCodes: payload.ImmuneResponseCodes,
MatchRegex: payload.Regex,
}
tests = append(tests, &test)
}
Expand Down
15 changes: 13 additions & 2 deletions src/pkg/tgen/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ type ApiTest struct {
Request *client.Request `json:"request"`
Path string `json:"path"`
PathWithParams string `json:"path_with_params"`
MatchRegex string `json:"match_regex"` // regex used in post processing for detecting injection

// Fields to be populated after making HTTP request
IsVulnerable bool `json:"is_vulnerable"`
IsDataLeak bool `json:"is_data_leak"`
Response *client.ConcurrentResponse `json:"response"`
Response *client.ConcurrentResponse `json:"concurrent_response"`

// Post Request Process
VulnerableResponseCodes []int `json:"vulnerable_response_codes"`
Expand All @@ -28,9 +29,19 @@ type InjectionConfig struct {
InBody bool
InHeader bool
InCookie bool
Payloads []string
Payloads []Payload

// for vulnerable ssrf endpoint inject endpoint in query param
// example: https://ssrf-website.com?offat_test_endpoint=/api/v1/users
InjectUriInQuery bool
}

// Struct used for injecting payloads while generating tests
type Payload struct {
InjText string // text to be injected

// Post Processors
VulnerableResponseCodes []int // status code indicating API endpoint is vulnerable
ImmuneResponseCodes []int // status code indicating API endpoint is not vulnerable
Regex string // regex to be used for post processing
}
16 changes: 16 additions & 0 deletions src/pkg/tgen/tgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type TGenHandler struct {
RunUnrestrictedHttpMethodTest bool
RunBasicSQLiTest bool
RunBasicSSRFTest bool
RunOsCommandInjectionTest bool

// SSRF Test related data
SsrfUrl string
Expand Down Expand Up @@ -46,6 +47,21 @@ func (t *TGenHandler) GenerateTests() []*ApiTest {
log.Info().Msgf("%d tests generated for Basic SQLI", len(newTests))
}

// Basic OS Command Injection Test
if t.RunOsCommandInjectionTest {
injectionConfig := InjectionConfig{
InBody: true,
InCookie: true,
InHeader: true,
InPath: true,
InQuery: true,
}
newTests := BasicOsCommandInjectionTest(t.BaseUrl, t.Doc, t.DefaultQueryParams, t.DefaultHeaders, injectionConfig)
tests = append(tests, newTests...)

log.Info().Msgf("%d tests generated for Basic OS Command Injection", len(newTests))
}

if t.RunBasicSSRFTest && utils.ValidateURL(t.SsrfUrl) {
injectionConfig := InjectionConfig{
InBody: true,
Expand Down
15 changes: 10 additions & 5 deletions src/pkg/trunner/postrunner/postrunner.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package postrunner

import (
"regexp"

_ "github.com/OWASP/OFFAT/src/pkg/logging"
"github.com/OWASP/OFFAT/src/pkg/tgen"
"github.com/OWASP/OFFAT/src/pkg/utils"
"github.com/rs/zerolog/log"
)

// removes immune endpoints from the api tests slice
Expand All @@ -29,13 +32,15 @@ func UpdateStatusCodeBasedResult(apiTests *[]*tgen.ApiTest) {
}

if len(apiTest.ImmuneResponseCodes) > 0 {
if !utils.SearchInSlice(apiTest.ImmuneResponseCodes, apiTest.Response.Response.StatusCode) {
apiTest.IsVulnerable = true
}
apiTest.IsVulnerable = !utils.SearchInSlice(apiTest.ImmuneResponseCodes, apiTest.Response.Response.StatusCode)
} else if len(apiTest.VulnerableResponseCodes) > 0 {
if utils.SearchInSlice(apiTest.VulnerableResponseCodes, apiTest.Response.Response.StatusCode) {
apiTest.IsVulnerable = true
apiTest.IsVulnerable = utils.SearchInSlice(apiTest.VulnerableResponseCodes, apiTest.Response.Response.StatusCode)
} else if len(apiTest.MatchRegex) > 0 {
isVuln, err := regexp.Match(apiTest.MatchRegex, apiTest.Response.Response.Body)
if err != nil {
log.Error().Stack().Err(err).Msg("failed to validate match regex against response body")
}
apiTest.IsVulnerable = isVuln
}
}
}

0 comments on commit d700b6b

Please sign in to comment.