unisec 0.0.4 → 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: 207fef6efc641ecf3ba11879d368cd2c6ac54f4d7001d948428c32a67e992e05
4
- data.tar.gz: bec8d1577d59b146747a1346794df62ea7928feb47ca3c64e669636f91cdb9fa
3
+ metadata.gz: 60d2859aca7324908f4894b8ca2cc99b145657e7ea68fbc51e11a1e787886a57
4
+ data.tar.gz: 4c426795faa520f02daec94f1cf14379a4a994a0902cc52640e2a358b210c28b
5
5
  SHA512:
6
- metadata.gz: cb406f1c0b27575d84b497d5cec9a994f05a33f816630ebacd5013594997394c5b5473575d1a241193bb1e586bd3a7510c80a7597d9995f35cbb0f5b09882987
7
- data.tar.gz: 994e93feae65577b462e85ac3450dbb4d8a7f6160d00f4a19fe21bae518de85846a2e4cb71668b7fee0c5afb2ea39932831d2e333e802ab048b6dbd5bd653cc1
6
+ metadata.gz: 8d5c67bf6bc1e76253971cc4b3bdccc513fb13fd3d3fa2dcdffa97329e09b45654b2f2f0a2c734e54c87bcffdb71adfa392a352ec757b6de0f761feaa8b73d28
7
+ data.tar.gz: 7be763bb5b2adb2139771c3e3f789c101f884209c7ca7e5488df18e97c6cac25578b11c34b451efaddeb11a9926fefeb935857da32a33e3de192b0e39cb46532
data/lib/unisec/bidi.rb CHANGED
@@ -129,6 +129,7 @@ module Unisec
129
129
  #
130
130
  # The light version displays only the spoof payload for easy piping with other commands.
131
131
  # @param light [Boolean] `true` = light display (displays only the spoof payload for easy piping with other commands), `false` (default) = full display.
132
+ # @return [String] CLI-ready output
132
133
  # @example
133
134
  # puts Unisec::Bidi::Spoof.new('noraj').display
134
135
  # # Target string: noraj
@@ -3,6 +3,7 @@
3
3
  require 'unisec/cli/bidi'
4
4
  require 'unisec/cli/confusables'
5
5
  require 'unisec/cli/hexdump'
6
+ require 'unisec/cli/normalization'
6
7
  require 'unisec/cli/properties'
7
8
  require 'unisec/cli/rugrep'
8
9
  require 'unisec/cli/size'
@@ -23,6 +24,8 @@ module Unisec
23
24
  register 'confusables randomize', Confusables::Randomize
24
25
  register 'grep', Grep
25
26
  register 'hexdump', Hexdump
27
+ register 'normalize all', Normalize::All
28
+ register 'normalize replace', Normalize::Replace
26
29
  register 'properties char', Properties::Char
27
30
  register 'properties codepoints', Properties::Codepoints
28
31
  register 'properties list', Properties::List
@@ -17,6 +17,9 @@ module Unisec
17
17
  # UTF-16LE: 4100 4300 4300 4500 4900 5300
18
18
  # UTF-32BE: 00000041 00000043 00000043 00000045 00000049 00000053
19
19
  # UTF-32LE: 41000000 43000000 43000000 45000000 49000000 53000000
20
+ #
21
+ # $unisec hexdump "ACCEIS" --enc utf16le
22
+ # 4100 4300 4300 4500 4900 5300
20
23
  # ```
21
24
  class Hexdump < Dry::CLI::Command
22
25
  desc 'Hexdump in all Unicode encodings'
@@ -35,7 +38,7 @@ module Unisec
35
38
  puts Unisec::Hexdump.new(input).display
36
39
  else
37
40
  # using send() is safe here thanks to the value whitelist
38
- puts puts Unisec::Hexdump.send(options[:enc], input)
41
+ puts Unisec::Hexdump.send(options[:enc], input)
39
42
  end
40
43
  end
41
44
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
4
+ require 'unisec'
5
+ require 'unisec/utils'
6
+
7
+ module Unisec
8
+ module CLI
9
+ module Commands
10
+ # CLI sub-commands `unisec normalize xxx` for the class {Unisec::Normalization} from the lib.
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
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -86,6 +86,7 @@ module Unisec
86
86
  end
87
87
 
88
88
  # Display a CLI-friendly output summurizing the hexdump in all Unicode encodings
89
+ # @return [String] CLI-ready output
89
90
  # @example
90
91
  # puts Unisec::Hexdump.new('K').display # =>
91
92
  # # UTF-8: e2 84 aa
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ctf_party'
4
+
5
+ module Unisec
6
+ # Normalization Forms
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
+
18
+ # Original input
19
+ # @return [String] untouched input
20
+ attr_reader :original
21
+
22
+ # Normalization Form C (NFC) - Canonical Decomposition, followed by Canonical Composition
23
+ # @return [String] input normalized with NFC
24
+ attr_reader :nfc
25
+
26
+ # Normalization Form KC (NFKC) - Compatibility Decomposition, followed by Canonical Composition
27
+ # @return [String] input normalized with NFKC
28
+ attr_reader :nfkc
29
+
30
+ # Normalization Form D (NFD) - Canonical Decomposition
31
+ # @return [String] input normalized with NFD
32
+ attr_reader :nfd
33
+
34
+ # Normalization Form KD (NFKD) - Compatibility Decomposition
35
+ # @return [String] input normalized with NFKD
36
+ attr_reader :nfkd
37
+
38
+ # Generate all normilzation forms for a given input
39
+ # @param str [String] the target string
40
+ # @return [nil]
41
+ def initialize(str)
42
+ @original = str
43
+ @nfc = Normalization.nfc(str)
44
+ @nfkc = Normalization.nfkc(str)
45
+ @nfd = Normalization.nfd(str)
46
+ @nfkd = Normalization.nfkd(str)
47
+ end
48
+
49
+ # Normalization Form C (NFC) - Canonical Decomposition, followed by Canonical Composition
50
+ # @param str [String] the target string
51
+ # @return [String] input normalized with NFC
52
+ def self.nfc(str)
53
+ str.unicode_normalize(:nfc)
54
+ end
55
+
56
+ # Normalization Form KC (NFKC) - Compatibility Decomposition, followed by Canonical Composition
57
+ # @param str [String] the target string
58
+ # @return [String] input normalized with NFKC
59
+ def self.nfkc(str)
60
+ str.unicode_normalize(:nfkc)
61
+ end
62
+
63
+ # Normalization Form D (NFD) - Canonical Decomposition
64
+ # @param str [String] the target string
65
+ # @return [String] input normalized with NFD
66
+ def self.nfd(str)
67
+ str.unicode_normalize(:nfd)
68
+ end
69
+
70
+ # Normalization Form KD (NFKD) - Compatibility Decomposition
71
+ # @param str [String] the target string
72
+ # @return [String] input normalized with NFKD
73
+ def self.nfkd(str)
74
+ str.unicode_normalize(:nfkd)
75
+ end
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
+
96
+ # Display a CLI-friendly output summurizing all normalization forms
97
+ # @return [String] CLI-ready output
98
+ # @example
99
+ # puts Unisec::Normalization.new("\u{1E9B 0323}").display
100
+ # # =>
101
+ # # Original: ẛ̣
102
+ # # U+1E9B U+0323
103
+ # # NFC: ẛ̣
104
+ # # U+1E9B U+0323
105
+ # # NFKC: ṩ
106
+ # # U+1E69
107
+ # # NFD: ẛ̣
108
+ # # U+017F U+0323 U+0307
109
+ # # NFKD: ṩ
110
+ # # U+0073 U+0323 U+0307
111
+ def display
112
+ colorize = lambda { |form_title, form_attr|
113
+ "#{Paint[form_title.to_s, :underline,
114
+ :bold]}: #{form_attr}\n #{Paint[Unisec::Properties.chars2codepoints(form_attr), :red]}\n"
115
+ }
116
+ colorize.call('Original', @original) +
117
+ colorize.call('NFC', @nfc) +
118
+ colorize.call('NFKC', @nfkc) +
119
+ colorize.call('NFD', @nfd) +
120
+ colorize.call('NFKD', @nfkd)
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
136
+ end
137
+ end
@@ -102,6 +102,7 @@ module Unisec
102
102
  # Display a CLI-friendly output summurizing everithing about the surrogates:
103
103
  # the corresponding character, code point, high and low surrogates
104
104
  # (each displayed as hexadecimal, decimal and binary).
105
+ # @return [String] CLI-ready output
105
106
  # @example
106
107
  # surr = Unisec::Surrogates.new(128169)
107
108
  # puts surr.display # =>
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Unisec
4
4
  # Version of unisec library and app
5
- VERSION = '0.0.4'
5
+ VERSION = '0.0.6'
6
6
  end
@@ -72,19 +72,19 @@ module Unisec
72
72
  # # …
73
73
  def self.display # rubocop:disable Metrics/AbcSize
74
74
  data = versions
75
- display = ->(node) { puts Paint[data[node][:label], :red, :bold].ljust(44) + " #{data[node][:version]}" }
76
- puts Paint['Unicode:', :underline]
77
- display.call(:ruby_unicode)
78
- display.call(:twittercldr_unicode)
79
- display.call(:unicodeconfusable_unicode)
80
- display.call(:twittercldr_icu)
81
- display.call(:twittercldr_cldr)
82
- display.call(:ruby_unicode_emoji)
83
- display.call(:ucd_derivedname)
84
- puts Paint["\nGems:", :underline]
85
- display.call(:unisec)
86
- display.call(:twittercldr)
87
- display.call(:unicodeconfusable)
75
+ colorize = ->(node) { Paint[data[node][:label], :red, :bold].ljust(44) + " #{data[node][:version]}\n" }
76
+ Paint["Unicode:\n", :underline] +
77
+ colorize.call(:ruby_unicode) +
78
+ colorize.call(:twittercldr_unicode) +
79
+ colorize.call(:unicodeconfusable_unicode) +
80
+ colorize.call(:twittercldr_icu) +
81
+ colorize.call(:twittercldr_cldr) +
82
+ colorize.call(:ruby_unicode_emoji) +
83
+ colorize.call(:ucd_derivedname) +
84
+ Paint["\nGems:\n", :underline] +
85
+ colorize.call(:unisec) +
86
+ colorize.call(:twittercldr) +
87
+ colorize.call(:unicodeconfusable)
88
88
  end
89
89
  end
90
90
  end
data/lib/unisec.rb CHANGED
@@ -5,6 +5,7 @@ require 'unisec/version'
5
5
  require 'unisec/bidi'
6
6
  require 'unisec/confusables'
7
7
  require 'unisec/hexdump'
8
+ require 'unisec/normalization'
8
9
  require 'unisec/properties'
9
10
  require 'unisec/rugrep'
10
11
  require 'unisec/size'
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.4
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-01-23 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
@@ -88,7 +88,7 @@ dependencies:
88
88
  version: '1.9'
89
89
  description: 'Toolkit for security research manipulating Unicode: confusables, homoglyphs,
90
90
  hexdump, code point, UTF-8, UTF-16, UTF-32, properties, regexp search, size, grapheme,
91
- surrogates, version, ICU, CLDR, UCD'
91
+ surrogates, version, ICU, CLDR, UCD, BiDi, normalization'
92
92
  email: alexandre.zanni@europe.com
93
93
  executables:
94
94
  - unisec
@@ -104,6 +104,7 @@ files:
104
104
  - lib/unisec/cli/cli.rb
105
105
  - lib/unisec/cli/confusables.rb
106
106
  - lib/unisec/cli/hexdump.rb
107
+ - lib/unisec/cli/normalization.rb
107
108
  - lib/unisec/cli/properties.rb
108
109
  - lib/unisec/cli/rugrep.rb
109
110
  - lib/unisec/cli/size.rb
@@ -111,6 +112,7 @@ files:
111
112
  - lib/unisec/cli/versions.rb
112
113
  - lib/unisec/confusables.rb
113
114
  - lib/unisec/hexdump.rb
115
+ - lib/unisec/normalization.rb
114
116
  - lib/unisec/properties.rb
115
117
  - lib/unisec/rugrep.rb
116
118
  - lib/unisec/size.rb