├── .github └── workflows │ └── codeql-analysis.yml ├── .goreleaser.yml ├── LICENSE ├── README.md ├── cmd └── pencode │ └── main.go ├── go.mod ├── pencode-completion.bash ├── pencode-completion.fish ├── pencode-completion.zsh └── pkg └── pencode ├── base64decode.go ├── base64encode.go ├── constants.go ├── encoders.go ├── hexdecoder.go ├── hexencoder.go ├── htmldecode.go ├── htmlencode.go ├── interfaces.go ├── jsondecode.go ├── jsonencode.go ├── md5.go ├── sha1.go ├── sha224.go ├── sha256.go ├── sha384.go ├── sha512.go ├── strings.go ├── template.go ├── unicodedecode.go ├── unicodeencodeall.go ├── urldecode.go ├── urlencode.go ├── urlencodeall.go ├── utf16beencoder.go ├── utf16leencoder.go ├── util.go ├── xmldecode.go └── xmlencode.go /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 3 * * 4' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['go'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | - go generate ./... 5 | builds: 6 | - id: pencode 7 | binary: pencode 8 | flags: 9 | - -trimpath 10 | env: 11 | - CGO_ENABLED=0 12 | asmflags: 13 | - all=-trimpath={{.Env.GOPATH}} 14 | gcflags: 15 | - all=-trimpath={{.Env.GOPATH}} 16 | ldflags: 17 | -s -w -extldflags '-static' 18 | goos: 19 | - linux 20 | - windows 21 | - darwin 22 | - freebsd 23 | - openbsd 24 | goarch: 25 | - amd64 26 | - 386 27 | - arm 28 | - arm64 29 | main: ./cmd/pencode 30 | ignore: 31 | - goos: darwin 32 | goarch: 386 33 | archives: 34 | - id: tgz 35 | format: tar.gz 36 | format_overrides: 37 | - goos: windows 38 | format: zip 39 | - goos: darwin 40 | format: zip 41 | 42 | checksum: 43 | name_template: 'checksums.txt' 44 | snapshot: 45 | name_template: "{{ .Tag }}-next" 46 | changelog: 47 | sort: asc 48 | filters: 49 | exclude: 50 | - '^docs:' 51 | - '^test:' 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Joona Hoikkala 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pencode - complex payload encoder 2 | 3 | Pencode is a tool that helps you to create payload encoding chains. It has been designed to be used in automation whereever 4 | it is required to apply multiple encodings to a payload (and possibly inserting the payload to a template in between). 5 | 6 | `pencode` can be used as a standalone command line tool or as a library for other Go programs. 7 | 8 | 9 | ## Installation 10 | ``` 11 | go install github.com/ffuf/pencode/cmd/pencode@latest 12 | ``` 13 | 14 | ### Usage 15 | 16 | ``` 17 | pencode - complex payload encoder v0.4 18 | 19 | Usage: ./pencode FUNC1 FUNC2 FUNC3... 20 | 21 | ./pencode reads input from stdin by default, which is typically piped from another process. 22 | 23 | OPTIONS: 24 | -input reads input from a file, line by line. 25 | 26 | ENCODERS 27 | b64encode - Base64 encoder 28 | hexencode - Hex string encoder 29 | htmlescape - HTML escape 30 | jsonescape - JSON escape 31 | unicodeencodeall - Unicode escape string encode (all characters) 32 | urlencode - URL encode reserved characters 33 | urlencodeall - URL encode all characters 34 | utf16 - UTF-16 encoder (Little Endian) 35 | utf16be - UTF-16 encoder (Big Endian) 36 | xmlescape - XML escape 37 | 38 | DECODERS 39 | b64decode - Base64 decoder 40 | hexdecode - Hex string decoder 41 | htmlunescape - HTML unescape 42 | jsonunescape - JSON unescape 43 | unicodedecode - Unicode escape string decode 44 | urldecode - URL decode 45 | xmlunescape - XML unescape 46 | 47 | HASHES 48 | md5 - MD5 sum 49 | sha1 - SHA1 checksum 50 | sha224 - SHA224 checksum 51 | sha256 - SHA256 checksum 52 | sha384 - SHA384 checksum 53 | sha512 - SHA512 checksum 54 | 55 | OTHER 56 | filename.tmpl - Replaces string #PAYLOAD# in content of a file that has .tmpl extension. 57 | lower - Convert string to lowercase 58 | upper - Convert string to uppercase 59 | 60 | ``` 61 | 62 | To urlencode, base64encode and hex encode a string: 63 | 64 | ``` 65 | $ echo 'what%ever'|pencode urlencode b64encode hexencode 66 | 64326868644355794e5756325a58493d 67 | ``` 68 | 69 | ### Templating 70 | 71 | Any command line parameter that is a file path ending with `.tmpl` is considered as a template file by 72 | pencode. It attempts to read the file content and to replace instances of a string `#PAYLOAD#` within the file with 73 | the input in the current encoder chain. 74 | 75 | ### Shell completion 76 | 77 | Pencode can provide tab completion for available encoders. Bash, Zsh, and Fish are supported. 78 | 79 | ``` 80 | $ pencode 81 | b64decode hexdecode unicodedecode urldecode urlencodeall utf16be 82 | ... 83 | ``` 84 | 85 | In order to activate shell completion, you need to inform your shell that completion is available for your script. 86 | 87 | #### Bash 88 | 89 | To get auto-complete working you need to `source` the `pencode-completion.bash` file in your `~/.bashrc` or similar: 90 | 91 | ``` 92 | source ~/path/to/pencode-completion.bash 93 | ``` 94 | 95 | #### Zsh 96 | 97 | To get auto-complete working you need to enable autocomplete _(not needed if you have Oh-My-Zsh)_ using `autoload -U compaudit && compinit` or by putting it into `~/.zshrc` 98 | 99 | Then `source` the `pencode-completion.zsh` file in your `.zshrc` or similar: 100 | 101 | ``` 102 | source ~/path/to/pencode-completion.zsh 103 | ``` 104 | 105 | #### Fish 106 | 107 | To get auto-complete working you need to `source` the `pencode-completion.fish` file to your config folder `~/.config/fish/completions/pencode.fish` or similar: 108 | 109 | ``` 110 | source ~/path/to/pencode-completion.fish 111 | ``` 112 | 113 | ### Usage as a library 114 | 115 | ```go 116 | package main 117 | 118 | import ( 119 | "fmt" 120 | 121 | "github.com/ffuf/pencode/pkg/pencode" 122 | ) 123 | 124 | func main() { 125 | inputdata := "Whatever you wish to run through the chain" 126 | # A slice of encoders in the preferred encoding chain execution order 127 | encoders := []string{ 128 | "utf16", 129 | "b64encode", 130 | } 131 | chain := pencode.NewChain() 132 | err := chain.Initialize(encoders) 133 | if err != nil { 134 | panic(err) 135 | } 136 | output, err := chain.Encode([]byte(inputdata)) 137 | if err != nil { 138 | panic(err) 139 | } 140 | fmt.Print(string(output)) 141 | } 142 | ``` 143 | 144 | ## License 145 | 146 | `pencode` is released under MIT license. See [LICENSE](https://github.com/ffuf/pencode/blob/master/LICENSE). 147 | -------------------------------------------------------------------------------- /cmd/pencode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "strings" 9 | 10 | "github.com/ffuf/pencode/pkg/pencode" 11 | ) 12 | 13 | func main() { 14 | chain := pencode.NewChain() 15 | 16 | inputWordlist := flag.String("input", "", "A wordlist to encode") 17 | 18 | flag.Usage = func() { 19 | fmt.Printf("pencode - complex payload encoder v%s\n\n", pencode.VERSION) 20 | fmt.Printf("Usage: %s FUNC1 FUNC2 FUNC3...\n\n", os.Args[0]) 21 | fmt.Printf("%s reads input from stdin by default, which is typically piped from another process.\n\n", os.Args[0]) 22 | fmt.Printf("OPTIONS:\n-input reads input from a file, line by line.\n\n") 23 | chain.Usage() 24 | } 25 | 26 | var listMode bool 27 | flag.BoolVar(&listMode, "list", false, "list available encoders") 28 | flag.Parse() 29 | 30 | if listMode { 31 | fmt.Println(strings.Join(chain.GetEncoders(), "\n")) 32 | os.Exit(1) 33 | } 34 | 35 | if len(os.Args) < 2 { 36 | flag.Usage() 37 | os.Exit(1) 38 | } 39 | 40 | err := chain.Initialize(flag.Args()) 41 | if err != nil { 42 | flag.Usage() 43 | fmt.Printf("\n[!] %s\n", err) 44 | os.Exit(1) 45 | } 46 | 47 | if *inputWordlist != "" { 48 | // read the input wordlist and output to stdout 49 | file, err := os.Open(*inputWordlist) 50 | 51 | if err != nil { 52 | fmt.Println(err) 53 | } 54 | 55 | defer file.Close() 56 | 57 | fs := bufio.NewScanner(file) 58 | fs.Split(bufio.ScanLines) 59 | 60 | for fs.Scan() { 61 | output, err := chain.Encode(fs.Bytes()) 62 | if err != nil { 63 | fmt.Printf(" [!] %s\n", err) 64 | } 65 | fmt.Println(string(output)) 66 | } 67 | } else { 68 | input := readInput() 69 | output, err := chain.Encode([]byte(input)) 70 | if err != nil { 71 | fmt.Printf(" [!] %s\n", err) 72 | } 73 | fmt.Print(string(output)) 74 | } 75 | } 76 | 77 | func readInput() string { 78 | input := os.Stdin 79 | defer input.Close() 80 | reader := bufio.NewScanner(input) 81 | 82 | data := "" 83 | for reader.Scan() { 84 | data = data + reader.Text() 85 | } 86 | return data 87 | } 88 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ffuf/pencode 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /pencode-completion.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | complete -W "\$(pencode -list)" pencode -------------------------------------------------------------------------------- /pencode-completion.fish: -------------------------------------------------------------------------------- 1 | complete -x -c pencode -a "(pencode -list)" 2 | -------------------------------------------------------------------------------- /pencode-completion.zsh: -------------------------------------------------------------------------------- 1 | compdef _pencode pencode 2 | 3 | function _pencode { 4 | _arguments "*: :($(pencode -list))" 5 | } -------------------------------------------------------------------------------- /pkg/pencode/base64decode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "encoding/base64" 5 | ) 6 | 7 | type Base64Decoder struct{} 8 | 9 | func (b Base64Decoder) Encode(input []byte) ([]byte, error) { 10 | output, err := base64.StdEncoding.DecodeString(string(input)) 11 | return output, err 12 | } 13 | 14 | func (b Base64Decoder) HelpText() string { 15 | return "Base64 decoder" 16 | } 17 | 18 | func (b Base64Decoder) Type() string { 19 | return "decoders" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/base64encode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "encoding/base64" 5 | ) 6 | 7 | type Base64Encoder struct{} 8 | 9 | func (b Base64Encoder) Encode(input []byte) ([]byte, error) { 10 | output := base64.StdEncoding.EncodeToString(input) 11 | return []byte(output), nil 12 | } 13 | 14 | func (b Base64Encoder) HelpText() string { 15 | return "Base64 encoder" 16 | } 17 | 18 | func (b Base64Encoder) Type() string { 19 | return "encoders" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/constants.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | const VERSION = "0.4" 4 | -------------------------------------------------------------------------------- /pkg/pencode/encoders.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "strings" 7 | ) 8 | 9 | var availableEncoders = map[string]Encoder{ 10 | "b64encode": Base64Encoder{}, 11 | "b64decode": Base64Decoder{}, 12 | "filename.tmpl": Template{}, 13 | "hexencode": HexEncoder{}, 14 | "hexdecode": HexDecoder{}, 15 | "htmlescape": HTMLEscaper{}, 16 | "htmlunescape": HTMLUnescaper{}, 17 | "jsonescape": JSONEscaper{}, 18 | "jsonunescape": JSONUnescaper{}, 19 | "lower": StrToLower{}, 20 | "md5": MD5Hasher{}, 21 | "sha1": SHA1Hasher{}, 22 | "sha224": SHA224Hasher{}, 23 | "sha256": SHA256Hasher{}, 24 | "sha384": SHA384Hasher{}, 25 | "sha512": SHA512Hasher{}, 26 | "unicodedecode": UnicodeDecode{}, 27 | "unicodeencodeall": UnicodeEncodeAll{}, 28 | "upper": StrToUpper{}, 29 | "urlencode": URLEncoder{}, 30 | "urldecode": URLDecoder{}, 31 | "urlencodeall": URLEncoderAll{}, 32 | "utf16": UTF16LEEncode{}, 33 | "utf16be": UTF16BEEncode{}, 34 | "xmlescape": XMLEscaper{}, 35 | "xmlunescape": XMLUnescaper{}, 36 | } 37 | 38 | type Chain struct { 39 | Encoders []Encoder 40 | initialized bool 41 | actions []string 42 | } 43 | 44 | func NewChain() *Chain { 45 | c := Chain{initialized: false} 46 | return &c 47 | } 48 | 49 | // Initialize loops through requested names for encoders and sets up the encoder chain. If an unknown encoder is 50 | // requested, error will be returned. 51 | func (c *Chain) Initialize(actions []string) error { 52 | c.actions = actions 53 | c.Encoders = make([]Encoder, 0) 54 | for _, a := range actions { 55 | if ok, err := c.HasEncoder(a); ok { 56 | // Templates are a bit special 57 | if isTemplate(a) { 58 | tenc, err := NewTemplateEncoder(a) 59 | if err != nil { 60 | return err 61 | } 62 | c.Encoders = append(c.Encoders, tenc) 63 | } else { 64 | c.Encoders = append(c.Encoders, availableEncoders[a]) 65 | } 66 | } else if err != nil { 67 | return err 68 | } else { 69 | return fmt.Errorf("Encoder %s requested but not found.\n", a) 70 | } 71 | } 72 | c.initialized = true 73 | return nil 74 | } 75 | 76 | func (c *Chain) Encode(input []byte) ([]byte, error) { 77 | var err error 78 | if !c.initialized { 79 | return []byte{}, fmt.Errorf("Encoder chain not initialized.\n") 80 | } 81 | for _, e := range c.Encoders { 82 | input, err = e.Encode(input) 83 | if err != nil { 84 | return []byte{}, err 85 | } 86 | } 87 | return input, nil 88 | } 89 | 90 | // HasEncoder returns true if encoder with a specified name is configured 91 | func (c *Chain) HasEncoder(name string) (bool, error) { 92 | if _, ok := availableEncoders[name]; ok { 93 | return true, nil 94 | } 95 | // Check for template 96 | if isTemplate(name) { 97 | return hasTemplate(name) 98 | } 99 | return false, nil 100 | } 101 | 102 | func (c *Chain) GetEncoders() []string { 103 | // Sort the encoders alphabetically 104 | names := make([]string, 0, len(availableEncoders)) 105 | for e := range availableEncoders { 106 | names = append(names, e) 107 | } 108 | sort.Strings(names) 109 | return names 110 | } 111 | 112 | // Usage prints the help string for each configured encoder 113 | func (c *Chain) Usage() { 114 | // Calculate maximum keyword length for nice help formatting 115 | max_length := 0 116 | for k := range availableEncoders { 117 | if len(k) > max_length { 118 | max_length = len(k) 119 | } 120 | } 121 | format := fmt.Sprintf(" %%-%ds- %%s\n", max_length+2) 122 | //names := c.GetEncoders() 123 | for _, t := range []string{"encoders", "decoders", "hashes", "other"} { 124 | fmt.Printf("%s\n", strings.ToUpper(t)) 125 | list := []string{} 126 | for n, e := range availableEncoders { 127 | if e.Type() == t { 128 | list = append(list, fmt.Sprintf(format, n, e.HelpText())) 129 | } 130 | } 131 | sort.Strings(list) 132 | for _, i := range list { 133 | fmt.Print(i) 134 | } 135 | fmt.Println() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /pkg/pencode/hexdecoder.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "encoding/hex" 5 | ) 6 | 7 | type HexDecoder struct{} 8 | 9 | func (h HexDecoder) Encode(input []byte) ([]byte, error) { 10 | cleaned := removeAllWhitespace(string(input)) 11 | return hex.DecodeString(cleaned) 12 | } 13 | 14 | func (h HexDecoder) HelpText() string { 15 | return "Hex string decoder" 16 | } 17 | 18 | func (h HexDecoder) Type() string { 19 | return "decoders" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/hexencoder.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "encoding/hex" 5 | ) 6 | 7 | type HexEncoder struct{} 8 | 9 | func (h HexEncoder) Encode(input []byte) ([]byte, error) { 10 | return []byte(hex.EncodeToString(input)), nil 11 | } 12 | 13 | func (h HexEncoder) HelpText() string { 14 | return "Hex string encoder" 15 | } 16 | 17 | func (h HexEncoder) Type() string { 18 | return "encoders" 19 | } 20 | -------------------------------------------------------------------------------- /pkg/pencode/htmldecode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import "html" 4 | 5 | type HTMLUnescaper struct{} 6 | 7 | func (u HTMLUnescaper) Encode(input []byte) ([]byte, error) { 8 | out := html.UnescapeString(string(input)) 9 | 10 | return []byte(out), nil 11 | } 12 | 13 | func (u HTMLUnescaper) HelpText() string { 14 | return "HTML unescape" 15 | } 16 | 17 | func (u HTMLUnescaper) Type() string { 18 | return "decoders" 19 | } 20 | -------------------------------------------------------------------------------- /pkg/pencode/htmlencode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import "html" 4 | 5 | type HTMLEscaper struct{} 6 | 7 | func (u HTMLEscaper) Encode(input []byte) ([]byte, error) { 8 | out := html.EscapeString(string(input)) 9 | 10 | return []byte(out), nil 11 | } 12 | 13 | func (u HTMLEscaper) HelpText() string { 14 | return "HTML escape" 15 | } 16 | 17 | func (u HTMLEscaper) Type() string { 18 | return "encoders" 19 | } 20 | -------------------------------------------------------------------------------- /pkg/pencode/interfaces.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | type Encoder interface { 4 | Encode([]byte) ([]byte, error) 5 | HelpText() string 6 | Type() string 7 | } 8 | -------------------------------------------------------------------------------- /pkg/pencode/jsondecode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import "encoding/json" 4 | 5 | type JSONUnescaper struct{} 6 | 7 | type JSONInput struct { 8 | Input string `json:"input"` 9 | } 10 | 11 | func (u JSONUnescaper) Encode(input []byte) ([]byte, error) { 12 | inputJson := `{"input":"` + string(input) + `"}` 13 | 14 | var out JSONInput 15 | if err := json.Unmarshal([]byte(inputJson), &out); err != nil { 16 | return []byte{}, err 17 | } 18 | 19 | return []byte(out.Input), nil 20 | } 21 | 22 | func (u JSONUnescaper) HelpText() string { 23 | return "JSON unescape" 24 | } 25 | 26 | func (u JSONUnescaper) Type() string { 27 | return "decoders" 28 | } 29 | -------------------------------------------------------------------------------- /pkg/pencode/jsonencode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | ) 7 | 8 | type JSONEscaper struct{} 9 | 10 | func (u JSONEscaper) Encode(input []byte) ([]byte, error) { 11 | buf := &bytes.Buffer{} 12 | enc := json.NewEncoder(buf) 13 | enc.SetEscapeHTML(false) // prevents encoding of < and > characters 14 | err := enc.Encode(string(input)) 15 | if err != nil { 16 | return []byte{}, err 17 | } 18 | 19 | output := buf.Bytes() 20 | return output[1 : len(output)-2], nil 21 | } 22 | 23 | func (u JSONEscaper) HelpText() string { 24 | return "JSON escape" 25 | } 26 | 27 | func (u JSONEscaper) Type() string { 28 | return "encoders" 29 | } 30 | -------------------------------------------------------------------------------- /pkg/pencode/md5.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | ) 7 | 8 | type MD5Hasher struct{} 9 | 10 | func (m MD5Hasher) Encode(input []byte) ([]byte, error) { 11 | return []byte(fmt.Sprintf("%x", md5.Sum(input))), nil 12 | } 13 | 14 | func (m MD5Hasher) HelpText() string { 15 | return "MD5 sum" 16 | } 17 | 18 | func (m MD5Hasher) Type() string { 19 | return "hashes" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/sha1.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | ) 7 | 8 | type SHA1Hasher struct{} 9 | 10 | func (m SHA1Hasher) Encode(input []byte) ([]byte, error) { 11 | return []byte(fmt.Sprintf("%x", sha1.Sum(input))), nil 12 | } 13 | 14 | func (m SHA1Hasher) HelpText() string { 15 | return "SHA1 checksum" 16 | } 17 | 18 | func (m SHA1Hasher) Type() string { 19 | return "hashes" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/sha224.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | ) 7 | 8 | type SHA224Hasher struct{} 9 | 10 | func (m SHA224Hasher) Encode(input []byte) ([]byte, error) { 11 | return []byte(fmt.Sprintf("%x", sha256.Sum224(input))), nil 12 | } 13 | 14 | func (m SHA224Hasher) HelpText() string { 15 | return "SHA224 checksum" 16 | } 17 | 18 | func (m SHA224Hasher) Type() string { 19 | return "hashes" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/sha256.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | ) 7 | 8 | type SHA256Hasher struct{} 9 | 10 | func (m SHA256Hasher) Encode(input []byte) ([]byte, error) { 11 | return []byte(fmt.Sprintf("%x", sha256.Sum256(input))), nil 12 | } 13 | 14 | func (m SHA256Hasher) HelpText() string { 15 | return "SHA256 checksum" 16 | } 17 | 18 | func (m SHA256Hasher) Type() string { 19 | return "hashes" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/sha384.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "crypto/sha512" 5 | "fmt" 6 | ) 7 | 8 | type SHA384Hasher struct{} 9 | 10 | func (m SHA384Hasher) Encode(input []byte) ([]byte, error) { 11 | return []byte(fmt.Sprintf("%x", sha512.Sum384(input))), nil 12 | } 13 | 14 | func (m SHA384Hasher) HelpText() string { 15 | return "SHA384 checksum" 16 | } 17 | 18 | func (m SHA384Hasher) Type() string { 19 | return "hashes" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/sha512.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "crypto/sha512" 5 | "fmt" 6 | ) 7 | 8 | type SHA512Hasher struct{} 9 | 10 | func (m SHA512Hasher) Encode(input []byte) ([]byte, error) { 11 | return []byte(fmt.Sprintf("%x", sha512.Sum512(input))), nil 12 | } 13 | 14 | func (m SHA512Hasher) HelpText() string { 15 | return "SHA512 checksum" 16 | } 17 | 18 | func (m SHA512Hasher) Type() string { 19 | return "hashes" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/pencode/strings.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type StrToUpper struct{} 8 | type StrToLower struct{} 9 | 10 | func (s StrToUpper) Encode(input []byte) ([]byte, error) { 11 | return []byte(strings.ToUpper(string(input))), nil 12 | } 13 | 14 | func (s StrToUpper) HelpText() string { 15 | return "Convert string to uppercase" 16 | } 17 | 18 | func (s StrToUpper) Type() string { 19 | return "other" 20 | } 21 | 22 | func (s StrToLower) Encode(input []byte) ([]byte, error) { 23 | return []byte(strings.ToLower(string(input))), nil 24 | } 25 | 26 | func (s StrToLower) HelpText() string { 27 | return "Convert string to lowercase" 28 | } 29 | 30 | func (s StrToLower) Type() string { 31 | return "other" 32 | } 33 | -------------------------------------------------------------------------------- /pkg/pencode/template.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | //isTemplate checks if the name ends with string ".tmpl" 10 | func isTemplate(name string) bool { 11 | return strings.HasSuffix(name, ".tmpl") 12 | } 13 | 14 | //hasTemplate checks if the name points to an existing file 15 | func hasTemplate(name string) (bool, error) { 16 | if _, err := os.Stat(name); err == nil { 17 | return true, nil 18 | } else { 19 | return false, err 20 | } 21 | } 22 | 23 | type Template struct { 24 | Data []byte 25 | Keyword string 26 | } 27 | 28 | func NewTemplateEncoder(name string) (Encoder, error) { 29 | var ok bool 30 | var err error 31 | t := Template{} 32 | // Using static keyword for now 33 | t.Keyword = "#PAYLOAD#" 34 | 35 | if ok, err = hasTemplate(name); ok { 36 | t.Data, err = ioutil.ReadFile(name) 37 | } 38 | return t, err 39 | } 40 | 41 | func (t Template) Encode(input []byte) ([]byte, error) { 42 | return []byte(strings.ReplaceAll(string(t.Data), t.Keyword, string(input))), nil 43 | } 44 | 45 | func (t Template) HelpText() string { 46 | return "Replaces string #PAYLOAD# in content of a file that has .tmpl extension." 47 | } 48 | 49 | func (t Template) Type() string { 50 | return "other" 51 | } 52 | -------------------------------------------------------------------------------- /pkg/pencode/unicodedecode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "unicode" 5 | "unicode/utf16" 6 | "unicode/utf8" 7 | ) 8 | 9 | type UnicodeDecode struct{} 10 | 11 | func (u UnicodeDecode) Encode(input []byte) ([]byte, error) { 12 | return unquoteBytes(input), nil 13 | } 14 | 15 | func (u UnicodeDecode) HelpText() string { 16 | return "Unicode escape string decode" 17 | } 18 | 19 | func (u UnicodeDecode) Type() string { 20 | return "decoders" 21 | } 22 | 23 | //This functionality is copied from encoding/json/decode.go with minor modifications 24 | func unquoteBytes(s []byte) []byte { 25 | b := make([]byte, len(s)+2*utf8.UTFMax) 26 | r := 0 27 | w := copy(b, s[0:r]) 28 | for r < len(s) { 29 | // Out of room? Can only happen if s is full of 30 | // malformed UTF-8 and we're replacing each 31 | // byte with RuneError. 32 | if w >= len(b)-2*utf8.UTFMax { 33 | nb := make([]byte, (len(b)+utf8.UTFMax)*2) 34 | copy(nb, b[0:w]) 35 | b = nb 36 | } 37 | switch c := s[r]; { 38 | case c == '\\': 39 | r++ 40 | if r >= len(s) { 41 | return b[0:w] 42 | } 43 | switch s[r] { 44 | default: 45 | return b[0:w] 46 | case '"', '\\', '/', '\'': 47 | b[w] = s[r] 48 | r++ 49 | w++ 50 | case 'b': 51 | b[w] = '\b' 52 | r++ 53 | w++ 54 | case 'f': 55 | b[w] = '\f' 56 | r++ 57 | w++ 58 | case 'n': 59 | b[w] = '\n' 60 | r++ 61 | w++ 62 | case 'r': 63 | b[w] = '\r' 64 | r++ 65 | w++ 66 | case 't': 67 | b[w] = '\t' 68 | r++ 69 | w++ 70 | case 'u': 71 | r-- 72 | rr := getu4(s[r:]) 73 | if rr < 0 { 74 | return b[0:w] 75 | } 76 | r += 6 77 | if utf16.IsSurrogate(rr) { 78 | rr1 := getu4(s[r:]) 79 | if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { 80 | // A valid pair; consume. 81 | r += 6 82 | w += utf8.EncodeRune(b[w:], dec) 83 | break 84 | } 85 | // Invalid surrogate; fall back to replacement rune. 86 | rr = unicode.ReplacementChar 87 | } 88 | w += utf8.EncodeRune(b[w:], rr) 89 | } 90 | // ASCII 91 | case c < utf8.RuneSelf: 92 | b[w] = c 93 | r++ 94 | w++ 95 | // Coerce to well-formed UTF-8. 96 | default: 97 | rr, size := utf8.DecodeRune(s[r:]) 98 | r += size 99 | w += utf8.EncodeRune(b[w:], rr) 100 | } 101 | } 102 | return b[0:w] 103 | } 104 | 105 | // getu4 decodes \uXXXX from the beginning of s, returning the hex value, 106 | // or it returns -1. 107 | func getu4(s []byte) rune { 108 | if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { 109 | return -1 110 | } 111 | var r rune 112 | for _, c := range s[2:6] { 113 | switch { 114 | case '0' <= c && c <= '9': 115 | c = c - '0' 116 | case 'a' <= c && c <= 'f': 117 | c = c - 'a' + 10 118 | case 'A' <= c && c <= 'F': 119 | c = c - 'A' + 10 120 | default: 121 | return -1 122 | } 123 | r = r*16 + rune(c) 124 | } 125 | return r 126 | } 127 | -------------------------------------------------------------------------------- /pkg/pencode/unicodeencodeall.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | type UnicodeEncodeAll struct{} 9 | 10 | func (u UnicodeEncodeAll) Encode(input []byte) ([]byte, error) { 11 | var b bytes.Buffer 12 | runes := []rune(string(input)) 13 | for _, r := range runes { 14 | b.WriteString("\\u") 15 | b.WriteString(fmt.Sprintf("%04x", int64(r))) 16 | } 17 | return b.Bytes(), nil 18 | } 19 | 20 | func (u UnicodeEncodeAll) HelpText() string { 21 | return "Unicode escape string encode (all characters)" 22 | } 23 | 24 | func (u UnicodeEncodeAll) Type() string { 25 | return "encoders" 26 | } 27 | -------------------------------------------------------------------------------- /pkg/pencode/urldecode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | type URLDecoder struct{} 8 | 9 | func (u URLDecoder) Encode(input []byte) ([]byte, error) { 10 | output, err := url.QueryUnescape(string(input)) 11 | if err != nil { 12 | return []byte{}, err 13 | } 14 | return []byte(output), err 15 | } 16 | 17 | func (u URLDecoder) HelpText() string { 18 | return "URL decode" 19 | } 20 | 21 | func (u URLDecoder) Type() string { 22 | return "decoders" 23 | } 24 | -------------------------------------------------------------------------------- /pkg/pencode/urlencode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | type URLEncoder struct{} 8 | 9 | func (u URLEncoder) Encode(input []byte) ([]byte, error) { 10 | return []byte(url.QueryEscape(string(input))), nil 11 | } 12 | 13 | func (u URLEncoder) HelpText() string { 14 | return "URL encode reserved characters" 15 | } 16 | 17 | func (u URLEncoder) Type() string { 18 | return "encoders" 19 | } 20 | -------------------------------------------------------------------------------- /pkg/pencode/urlencodeall.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | const upperhex = "0123456789ABCDEF" 8 | 9 | type URLEncoderAll struct{} 10 | 11 | func (u URLEncoderAll) Encode(input []byte) ([]byte, error) { 12 | var buf bytes.Buffer 13 | for _, c := range input { 14 | // Add "%" 15 | buf.WriteByte(37) 16 | buf.WriteByte(upperhex[c>>4]) 17 | buf.WriteByte(upperhex[c&15]) 18 | } 19 | return buf.Bytes(), nil 20 | } 21 | 22 | func (u URLEncoderAll) HelpText() string { 23 | return "URL encode all characters" 24 | } 25 | 26 | func (u URLEncoderAll) Type() string { 27 | return "encoders" 28 | } 29 | -------------------------------------------------------------------------------- /pkg/pencode/utf16beencoder.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "unicode/utf16" 7 | ) 8 | 9 | type UTF16BEEncode struct{} 10 | 11 | func (u UTF16BEEncode) Encode(input []byte) ([]byte, error) { 12 | var b bytes.Buffer 13 | runes := []rune(string(input)) 14 | utf16ints := utf16.Encode(runes) 15 | for _, r := range utf16ints { 16 | tmp := make([]byte, 2) 17 | binary.BigEndian.PutUint16(tmp, r) 18 | b.Write(tmp) 19 | } 20 | return b.Bytes(), nil 21 | } 22 | 23 | func (u UTF16BEEncode) HelpText() string { 24 | return "UTF-16 encoder (Big Endian)" 25 | } 26 | 27 | func (u UTF16BEEncode) Type() string { 28 | return "encoders" 29 | } 30 | -------------------------------------------------------------------------------- /pkg/pencode/utf16leencoder.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "unicode/utf16" 7 | ) 8 | 9 | type UTF16LEEncode struct{} 10 | 11 | func (u UTF16LEEncode) Encode(input []byte) ([]byte, error) { 12 | var b bytes.Buffer 13 | runes := []rune(string(input)) 14 | utf16ints := utf16.Encode(runes) 15 | for _, r := range utf16ints { 16 | tmp := make([]byte, 2) 17 | binary.LittleEndian.PutUint16(tmp, r) 18 | b.Write(tmp) 19 | } 20 | return b.Bytes(), nil 21 | } 22 | 23 | func (u UTF16LEEncode) HelpText() string { 24 | return "UTF-16 encoder (Little Endian)" 25 | } 26 | 27 | func (u UTF16LEEncode) Type() string { 28 | return "encoders" 29 | } 30 | -------------------------------------------------------------------------------- /pkg/pencode/util.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | ) 7 | 8 | func removeAllWhitespace(input string) string { 9 | var b strings.Builder 10 | b.Grow(len(input)) 11 | for _, ch := range input { 12 | if !unicode.IsSpace(ch) { 13 | b.WriteRune(ch) 14 | } 15 | } 16 | return b.String() 17 | } 18 | -------------------------------------------------------------------------------- /pkg/pencode/xmldecode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import "encoding/xml" 4 | 5 | type XMLUnescaper struct{} 6 | 7 | type XMLInput struct { 8 | Input string `xml:"input"` 9 | } 10 | 11 | func (u XMLUnescaper) Encode(input []byte) ([]byte, error) { 12 | inputXML := `` + string(input) + `` 13 | 14 | var out XMLInput 15 | if err := xml.Unmarshal([]byte(inputXML), &out); err != nil { 16 | return []byte{}, err 17 | } 18 | 19 | return []byte(out.Input), nil 20 | } 21 | 22 | func (u XMLUnescaper) HelpText() string { 23 | return "XML unescape" 24 | } 25 | 26 | func (u XMLUnescaper) Type() string { 27 | return "decoders" 28 | } 29 | -------------------------------------------------------------------------------- /pkg/pencode/xmlencode.go: -------------------------------------------------------------------------------- 1 | package pencode 2 | 3 | import ( 4 | "bytes" 5 | "encoding/xml" 6 | ) 7 | 8 | type XMLEscaper struct{} 9 | 10 | func (u XMLEscaper) Encode(input []byte) ([]byte, error) { 11 | buf := &bytes.Buffer{} 12 | 13 | if err := xml.EscapeText(buf, input); err != nil { 14 | return []byte{}, err 15 | } 16 | 17 | output := buf.Bytes() 18 | return output, nil 19 | } 20 | 21 | func (u XMLEscaper) HelpText() string { 22 | return "XML escape" 23 | } 24 | 25 | func (u XMLEscaper) Type() string { 26 | return "encoders" 27 | } 28 | --------------------------------------------------------------------------------