unisec 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b6f74de3dc3d0f9aebac59ff68bc5731b7185e9e73e86e87477c23408900452
4
- data.tar.gz: 910be7b95b71022f352cc6d612c7752fe2ea13a0e6e3dc89aca4566cb1879569
3
+ metadata.gz: 60d2859aca7324908f4894b8ca2cc99b145657e7ea68fbc51e11a1e787886a57
4
+ data.tar.gz: 4c426795faa520f02daec94f1cf14379a4a994a0902cc52640e2a358b210c28b
5
5
  SHA512:
6
- metadata.gz: 47b730a884a30979be5968d90a30bc214ec93725383f20c48a02ec1090ce64e6043f95427c7ed79ebc31b3b4e1d3b66e01c7b0d63f936793f6c8eb008ce5f9fe
7
- data.tar.gz: be9ca5cc40baaf9cd56141a3a8e41f5709c6071d5e7797ebd64a1daf231671e6835c168bce53f540e72ead4f68fc93bfd2d211764ba474e1cd8a4ca1bf5e7a65
6
+ metadata.gz: 8d5c67bf6bc1e76253971cc4b3bdccc513fb13fd3d3fa2dcdffa97329e09b45654b2f2f0a2c734e54c87bcffdb71adfa392a352ec757b6de0f761feaa8b73d28
7
+ data.tar.gz: 7be763bb5b2adb2139771c3e3f789c101f884209c7ca7e5488df18e97c6cac25578b11c34b451efaddeb11a9926fefeb935857da32a33e3de192b0e39cb46532
@@ -24,7 +24,8 @@ module Unisec
24
24
  register 'confusables randomize', Confusables::Randomize
25
25
  register 'grep', Grep
26
26
  register 'hexdump', Hexdump
27
- register 'normalize', Normalize
27
+ register 'normalize all', Normalize::All
28
+ register 'normalize replace', Normalize::Replace
28
29
  register 'properties char', Properties::Char
29
30
  register 'properties codepoints', Properties::Codepoints
30
31
  register 'properties list', Properties::List
@@ -8,45 +8,77 @@ module Unisec
8
8
  module CLI
9
9
  module Commands
10
10
  # CLI sub-commands `unisec normalize xxx` for the class {Unisec::Normalization} from the lib.
11
- #
12
- # Command `unisec normalize "example"`
13
- #
14
- # Example:
15
- #
16
- # ```plaintext
17
- # ➜ unisec normalize ẛ̣
18
- # Original: ẛ̣
19
- # U+1E9B U+0323
20
- # NFC: ẛ̣
21
- # U+1E9B U+0323
22
- # NFKC: ṩ
23
- # U+1E69
24
- # NFD: ẛ̣
25
- # U+017F U+0323 U+0307
26
- # NFKD: ṩ
27
- # U+0073 U+0323 U+0307
28
- #
29
- # ➜ unisec normalize ẛ̣ --form nfkd
30
- # ṩ
31
- # ```
32
- class Normalize < Dry::CLI::Command
33
- desc 'Normalize in all forms'
34
-
35
- argument :input, required: true,
36
- desc: 'String input. Read from STDIN if equal to -.'
37
-
38
- option :form, default: nil, values: %w[nfc nfkc nfd nfkd],
39
- desc: 'Output only in the specified normalization form.'
40
-
41
- # Normalize in all forms
42
- # @param input [String] Input string to normalize
43
- def call(input: nil, **options)
44
- input = $stdin.read.chomp if input == '-'
45
- if options[:form].nil?
46
- puts Unisec::Normalization.new(input).display
47
- else
48
- # using send() is safe here thanks to the value whitelist
49
- puts Unisec::Normalization.send(options[:form], input)
11
+ module Normalize
12
+ # Command `unisec normalize all "example"`
13
+ #
14
+ # Example:
15
+ #
16
+ # ```plaintext
17
+ # ➜ unisec normalize all ẛ̣
18
+ # Original: ẛ̣
19
+ # U+1E9B U+0323
20
+ # NFC: ẛ̣
21
+ # U+1E9B U+0323
22
+ # NFKC: ṩ
23
+ # U+1E69
24
+ # NFD: ẛ̣
25
+ # U+017F U+0323 U+0307
26
+ # NFKD: ṩ
27
+ # U+0073 U+0323 U+0307
28
+ #
29
+ # ➜ unisec normalize all ẛ̣ --form nfkd
30
+ # ṩ
31
+ # ```
32
+ class All < Dry::CLI::Command
33
+ desc 'Normalize in all forms'
34
+
35
+ argument :input, required: true,
36
+ desc: 'String input. Read from STDIN if equal to -.'
37
+
38
+ option :form, default: nil, values: %w[nfc nfkc nfd nfkd],
39
+ desc: 'Output only in the specified normalization form.'
40
+
41
+ # Normalize in all forms
42
+ # @param input [String] Input string to normalize
43
+ def call(input: nil, **options)
44
+ input = $stdin.read.chomp if input == '-'
45
+ if options[:form].nil?
46
+ puts Unisec::Normalization.new(input).display
47
+ else
48
+ # using send() is safe here thanks to the value whitelist
49
+ puts Unisec::Normalization.send(options[:form], input)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Command `unisec normalize replace "example"`
55
+ #
56
+ # Example:
57
+ #
58
+ # ```plaintext
59
+ # ➜ unisec normalize replace "<svg onload=\"alert('XSS')\">"
60
+ # Original: <svg onload="alert('XSS')">
61
+ # U+003C U+0073 U+0076 U+0067 U+0020 U+006F U+006E U+006C U+006F U+0061 U+0064 U+003D U+0022 U+0061 U+006C U+0065 U+0072 U+0074 U+0028 U+0027 U+0058 U+0053 U+0053 U+0027 U+0029 U+0022 U+003E
62
+ # Bypass payload: ﹤svg onload="alert('XSS')"﹥
63
+ # U+FE64 U+0073 U+0076 U+0067 U+0020 U+006F U+006E U+006C U+006F U+0061 U+0064 U+003D U+FF02 U+0061 U+006C U+0065 U+0072 U+0074 U+0028 U+FF07 U+0058 U+0053 U+0053 U+FF07 U+0029 U+FF02 U+FE65
64
+ # NFKC: <svg onload="alert('XSS')">
65
+ # U+003C U+0073 U+0076 U+0067 U+0020 U+006F U+006E U+006C U+006F U+0061 U+0064 U+003D U+0022 U+0061 U+006C U+0065 U+0072 U+0074 U+0028 U+0027 U+0058 U+0053 U+0053 U+0027 U+0029 U+0022 U+003E
66
+ # NFKD: <svg onload="alert('XSS')">
67
+ # U+003C U+0073 U+0076 U+0067 U+0020 U+006F U+006E U+006C U+006F U+0061 U+0064 U+003D U+0022 U+0061 U+006C U+0065 U+0072 U+0074 U+0028 U+0027 U+0058 U+0053 U+0053 U+0027 U+0029 U+0022 U+003E
68
+ #
69
+ # ➜ echo -n "<svg onload=\"alert('XSS')\">" | unisec normalize replace -
70
+ # ```
71
+ class Replace < Dry::CLI::Command
72
+ desc 'Prepare a XSS payload for HTML escape bypass (HTML escape followed by NFKC / NFKD normalization)'
73
+
74
+ argument :input, required: true,
75
+ desc: 'String input. Read from STDIN if equal to -.'
76
+
77
+ # Prepare a XSS payload for HTML escape bypass (HTML escape followed by NFKC / NFKD normalization)
78
+ # @param input [String] Input string to normalize
79
+ def call(input: nil, **_options)
80
+ input = $stdin.read.chomp if input == '-'
81
+ puts Unisec::Normalization.new(input).display_replace
50
82
  end
51
83
  end
52
84
  end
@@ -5,6 +5,16 @@ require 'ctf_party'
5
5
  module Unisec
6
6
  # Normalization Forms
7
7
  class Normalization
8
+ # HTML escapable characters mapped with their Unicode counterparts that will
9
+ # cast to themself after applying normalization forms using compatibility mode.
10
+ HTML_ESCAPE_BYPASS = {
11
+ '<' => ['﹤', '<'],
12
+ '>' => ['﹥', '>'],
13
+ '"' => ['"'],
14
+ "'" => ['''],
15
+ '&' => ['﹠', '&']
16
+ }.freeze
17
+
8
18
  # Original input
9
19
  # @return [String] untouched input
10
20
  attr_reader :original
@@ -64,6 +74,25 @@ module Unisec
64
74
  str.unicode_normalize(:nfkd)
65
75
  end
66
76
 
77
+ # Replace HTML escapable characters with their Unicode counterparts that will
78
+ # cast to themself after applying normalization forms using compatibility mode.
79
+ # Usefull for XSS, to bypass HTML escape.
80
+ # If several values are possible, one is picked randomly.
81
+ # @param str [String] the target string
82
+ # @return [String] escaped input
83
+ def self.replace_bypass(str)
84
+ str = str.dup
85
+ HTML_ESCAPE_BYPASS.each do |k, v|
86
+ str.gsub!(k, v.sample)
87
+ end
88
+ str
89
+ end
90
+
91
+ # Instance version of {Normalization.replace_bypass}.
92
+ def replace_bypass
93
+ Normalization.replace_bypass(@original)
94
+ end
95
+
67
96
  # Display a CLI-friendly output summurizing all normalization forms
68
97
  # @return [String] CLI-ready output
69
98
  # @example
@@ -90,5 +119,19 @@ module Unisec
90
119
  colorize.call('NFD', @nfd) +
91
120
  colorize.call('NFKD', @nfkd)
92
121
  end
122
+
123
+ # Display a CLI-friendly output of the XSS payload to bypass HTML escape and
124
+ # what it does once normalized in NFKC & NFKD.
125
+ def display_replace
126
+ colorize = lambda { |form_title, form_attr|
127
+ "#{Paint[form_title.to_s, :underline,
128
+ :bold]}: #{form_attr}\n #{Paint[Unisec::Properties.chars2codepoints(form_attr), :red]}\n"
129
+ }
130
+ payload = replace_bypass
131
+ colorize.call('Original', @original) +
132
+ colorize.call('Bypass payload', payload) +
133
+ colorize.call('NFKC', Normalization.nfkc(payload)) +
134
+ colorize.call('NFKD', Normalization.nfkd(payload))
135
+ end
93
136
  end
94
137
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Unisec
4
4
  # Version of unisec library and app
5
- VERSION = '0.0.5'
5
+ VERSION = '0.0.6'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unisec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre ZANNI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-16 00:00:00.000000000 Z
11
+ date: 2024-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ctf-party