unisec 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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