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 +4 -4
- data/lib/unisec/cli/cli.rb +2 -1
- data/lib/unisec/cli/normalization.rb +71 -39
- data/lib/unisec/normalization.rb +43 -0
- data/lib/unisec/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60d2859aca7324908f4894b8ca2cc99b145657e7ea68fbc51e11a1e787886a57
|
4
|
+
data.tar.gz: 4c426795faa520f02daec94f1cf14379a4a994a0902cc52640e2a358b210c28b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d5c67bf6bc1e76253971cc4b3bdccc513fb13fd3d3fa2dcdffa97329e09b45654b2f2f0a2c734e54c87bcffdb71adfa392a352ec757b6de0f761feaa8b73d28
|
7
|
+
data.tar.gz: 7be763bb5b2adb2139771c3e3f789c101f884209c7ca7e5488df18e97c6cac25578b11c34b451efaddeb11a9926fefeb935857da32a33e3de192b0e39cb46532
|
data/lib/unisec/cli/cli.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
data/lib/unisec/normalization.rb
CHANGED
@@ -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
|
data/lib/unisec/version.rb
CHANGED
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
|
+
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-
|
11
|
+
date: 2024-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ctf-party
|