unisec 0.0.3 → 0.0.4

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: d93010e590d0ae588dfb31364036652eb19d5ab17b504fbb851495a2ca71d18d
4
- data.tar.gz: b76c392938f2c804626b81efa227aaf44a65abec7ac63558144644e851b8d475
3
+ metadata.gz: 207fef6efc641ecf3ba11879d368cd2c6ac54f4d7001d948428c32a67e992e05
4
+ data.tar.gz: bec8d1577d59b146747a1346794df62ea7928feb47ca3c64e669636f91cdb9fa
5
5
  SHA512:
6
- metadata.gz: bde0eb13806b6e6ac2a12d8889c7ef236bdc923e334d59409257a95101d48702aefe10cf83b6a3a2c5bd5d12ebe0170f52cfcaff2cc9c2177db8c455f9a5c6b1
7
- data.tar.gz: 3f3d376b4549b846fee8af810ed134aca724eb377853b64bbe3f75562a33f4b799f7808554ad73211a4158a93feb989e4fc7ecc511babf233f7a6ebf358a17fb
6
+ metadata.gz: cb406f1c0b27575d84b497d5cec9a994f05a33f816630ebacd5013594997394c5b5473575d1a241193bb1e586bd3a7510c80a7597d9995f35cbb0f5b09882987
7
+ data.tar.gz: 994e93feae65577b462e85ac3450dbb4d8a7f6160d00f4a19fe21bae518de85846a2e4cb71668b7fee0c5afb2ea39932831d2e333e802ab048b6dbd5bd653cc1
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unisec/utils'
4
+ require 'ctf_party'
5
+
6
+ module Unisec
7
+ # Manipulation of bidirectional related content
8
+ class Bidi
9
+ # Attack using BiDi code points like RtLO, for example, for spoofing a domain name or a file name
10
+ class Spoof
11
+ # The target string to spoof (eg. URL, domain or file name)
12
+ # @return [String] the target string
13
+ attr_reader :target_display
14
+
15
+ # Set a new target string to spoof
16
+ #
17
+ # It will automatically set `@spoof_string` and `@spoof_payload` as well.
18
+ # @param input [String] the target string
19
+ # @param opts [Hash] optional parameters, see {Spoof.bidi_affix}
20
+ # @return [String] the target string
21
+ def set_target_display(input, **opts)
22
+ @target_display = input
23
+ @spoof_string = reverse(**opts)
24
+ @spoof_payload = bidi_affix(**opts)
25
+ @target_display
26
+ end
27
+
28
+ # The string for the spoofing attack without the BiDi characters
29
+ # @return [String] the spoof string (without BiDi)
30
+ attr_reader :spoof_string
31
+
32
+ # The string for the spoofing attack with the BiDi characters. (Spoof payload = spoof string + BiDi)
33
+ # @return [String] the spoof string (with BiDi)
34
+ attr_reader :spoof_payload
35
+
36
+ # @param input [String] the target string
37
+ # @param opts [Hash] optional parameters, see {Spoof.bidi_affix}
38
+ # @example
39
+ # bd = Unisec::Bidi::Spoof.new('https://moc.example.org//:sptth')
40
+ # bd.target_display # => "https://moc.example.org//:sptth"
41
+ # bd.spoof_string # => "https://gro.elpmaxe.com//:sptth"
42
+ # bd.spoof_payload => "‮https://gro.elpmaxe.com//:sptth‬"
43
+ def initialize(input, **opts)
44
+ opts[:index] ||= opts[:infix_pos]
45
+
46
+ @target_display = input
47
+ @spoof_string = reverse(**opts)
48
+ @spoof_payload = bidi_affix(**opts)
49
+ end
50
+
51
+ # Reverse the (sub)-string (grapheme cluster aware)
52
+ # @param target [String] string to reverse
53
+ # @param opts [Hash] optional parameters
54
+ # @option opts [String] :index Index at which the revese starts (before this position will be left untouched)
55
+ # @return [String] the reversed string
56
+ # @example
57
+ # Unisec::Bidi::Spoof.reverse('document_anntxt.exe', index: 12)
58
+ # # => "document_annexe.txt"
59
+ #
60
+ # Unisec::Bidi::Spoof.reverse("🇫🇷🐓")
61
+ # # => "🐓🇫🇷"
62
+ def self.reverse(target, **opts)
63
+ opts[:index] ||= 0
64
+
65
+ target[0...opts[:index]] + Unisec::Utils::String.grapheme_reverse(target[opts[:index]..])
66
+ end
67
+
68
+ # Call {Spoof.reverse} with `@target_display` as default input (target).
69
+ def reverse(**opts)
70
+ Spoof.reverse(@target_display, **opts)
71
+ end
72
+
73
+ # Inject BiDi characters into the input string
74
+ # @param input [String] input string
75
+ # @param opts [Hash] optional parameters
76
+ # @option opts [String] :prefix Prefix Bidi. Default: RLO (U+202E).
77
+ # @option opts [String] :suffix Suffix Bidi. Default: PDF (U+202C).
78
+ # @option opts [String] :infix_bidi Bidi injected at a chosen position. Default: none (empty string).
79
+ # @option opts [String] :infix_pos Position (index) where to inject an extra BiDi. Default: 0.
80
+ # @return [String] spoof payload (input string with injected BiDi)
81
+ # @example
82
+ # # By default inject a RLO prefix, a PDF suffix and no infix.
83
+ # Unisec::Bidi::Spoof.bidi_affix('acceis')
84
+ # # => "‮acceis‬"
85
+ #
86
+ # # RLI ... PDI
87
+ # Unisec::Bidi::Spoof.bidi_affix('acceis', prefix: "\u{2067}", suffix: "\u{2069}")
88
+ # # => "⁧acceis⁩"
89
+ #
90
+ # # RLE ... PDF
91
+ # Unisec::Bidi::Spoof.bidi_affix('acceis', prefix: "\u{202B}", suffix: "\u{202C}")
92
+ # # => "‫acceis‬"
93
+ #
94
+ # # RLO ... PDF
95
+ # Unisec::Bidi::Spoof.bidi_affix('https://moc.example.org//:sptth', prefix: "\u{202E}", suffix: "\u{202C}")
96
+ # # => "‮https://moc.example.org//:sptth‬"
97
+ #
98
+ # # FSI RLO ... PDF PDI
99
+ # Unisec::Bidi::Spoof.bidi_affix('https://moc.example.org//:sptth', prefix: "\u{2068 202E}", suffix: "\u{202C 2069}")
100
+ # # => "⁨‮https://moc.example.org//:sptth‬⁩"
101
+ #
102
+ # # RLM ...
103
+ # Unisec::Bidi::Spoof.bidi_affix('unicode', prefix: "\u{200F}", suffix: '')
104
+ # # => "‏unicode"
105
+ #
106
+ # # For file name spoofing, it is useful to be able to inject just a RLO before the fake extension
107
+ # # so we can void the prefix and suffix and just set the position of an infix
108
+ # ex = Unisec::Bidi::Spoof.bidi_affix('document_anntxt.exe', prefix: '', suffix: '', infix_bidi: "\u{202E}", infix_pos: 12)
109
+ # # => "document_ann‮txt.exe"
110
+ # puts ex
111
+ # # document_ann‮txt.exe
112
+ def self.bidi_affix(input, **opts)
113
+ opts[:prefix] ||= "\u{202E}" # RLO
114
+ opts[:suffix] ||= "\u{202C}" # PDF
115
+ opts[:infix_bidi] ||= ''
116
+ opts[:infix_pos] ||= 0
117
+
118
+ out = "#{opts[:prefix]}#{input}#{opts[:suffix]}"
119
+ out.insert(opts[:infix_pos], opts[:infix_bidi])
120
+ out
121
+ end
122
+
123
+ # Call {Spoof.bidi_affix} with `@spoof_string` as input.
124
+ def bidi_affix(**opts)
125
+ Spoof.bidi_affix(@spoof_string, **opts)
126
+ end
127
+
128
+ # Display a CLI-friendly output summurizing the spoof payload
129
+ #
130
+ # The light version displays only the spoof payload for easy piping with other commands.
131
+ # @param light [Boolean] `true` = light display (displays only the spoof payload for easy piping with other commands), `false` (default) = full display.
132
+ # @example
133
+ # puts Unisec::Bidi::Spoof.new('noraj').display
134
+ # # Target string: noraj
135
+ # # Spoof payload (display) ⚠: ‮jaron‬
136
+ # # Spoof string 🛈: jaron
137
+ # # Spoof payload (hex): e280ae6a61726f6ee280ac
138
+ # # Spoof payload (hex, escaped): \xe2\x80\xae\x6a\x61\x72\x6f\x6e\xe2\x80\xac
139
+ # # Spoof payload (base64): 4oCuamFyb27igKw=
140
+ # # Spoof payload (urlencode): %E2%80%AEjaron%E2%80%AC
141
+ # # Spoof payload (code points): U+202E U+006A U+0061 U+0072 U+006F U+006E U+202C
142
+ # #
143
+ # #
144
+ # #
145
+ # # ⚠: for the spoof payload to display correctly, be sure your VTE has RTL support, e.g. see https://wiki.archlinux.org/title/Bidirectional_text#Terminal.
146
+ # # 🛈: Does not contain the BiDi character (e.g. RtLO).
147
+ #
148
+ # puts Unisec::Bidi::Spoof.new('noraj').display(light: true)
149
+ # # ‮jaron‬
150
+ def display(light: false)
151
+ if light == false # full display
152
+ "Target string: #{@target_display}\n" \
153
+ "Spoof payload (display) ⚠: #{@spoof_payload}\n" \
154
+ "Spoof string 🛈: #{@spoof_string}\n" \
155
+ "Spoof payload (hex): #{@spoof_payload.to_hex}\n" \
156
+ "Spoof payload (hex, escaped): #{@spoof_payload.to_hex(prefixall: '\\x')}\n" \
157
+ "Spoof payload (base64): #{@spoof_payload.to_b64}\n" \
158
+ "Spoof payload (urlencode): #{@spoof_payload.urlencode}\n" \
159
+ "Spoof payload (code points): #{Unisec::Properties.chars2codepoints(@spoof_payload)}\n" \
160
+ "\n\n\n" \
161
+ '⚠: for the spoof payload to display correctly, be sure your VTE has RTL support, ' \
162
+ "e.g. see https://wiki.archlinux.org/title/Bidirectional_text#Terminal.\n" \
163
+ '🛈: Does not contain the BiDi character (e.g. RtLO).'
164
+ else # light display
165
+ @spoof_payload
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,63 @@
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 bidi xxx` for the class {Unisec::Bidi} from the lib.
11
+ module Bidi
12
+ # Command `unisec bidi spoof`
13
+ #
14
+ # Example:
15
+ #
16
+ # ```plaintext
17
+ # $ unisec bidi spoof noraj
18
+ # Target string: noraj
19
+ # Spoof payload (display) ⚠: ‮jaron‬
20
+ # Spoof string 🛈: jaron
21
+ # Spoof payload (hex): e280ae6a61726f6ee280ac
22
+ # Spoof payload (hex, escaped): \xe2\x80\xae\x6a\x61\x72\x6f\x6e\xe2\x80\xac
23
+ # Spoof payload (base64): 4oCuamFyb27igKw=
24
+ # Spoof payload (urlencode): %E2%80%AEjaron%E2%80%AC
25
+ # Spoof payload (code points): U+202E U+006A U+0061 U+0072 U+006F U+006E U+202C
26
+ #
27
+ #
28
+ #
29
+ # ⚠: for the spoof payload to display correctly, be sure your VTE has RTL support, e.g. see https://wiki.archlinux.org/title/Bidirectional_text#Terminal.
30
+ # 🛈: Does not contain the BiDi character (e.g. RtLO).
31
+ #
32
+ # $ unisec bidi spoof 'document_annexe.txt' --prefix '' --suffix '' --infix-bidi $'\U202E' --infix-pos 12 --light=true
33
+ # document_ann‮txt.exe
34
+ # ```
35
+ class Spoof < Dry::CLI::Command
36
+ desc 'Craft a payload for BiDi attacks (for example, for spoofing a domain name or a file name)'
37
+
38
+ argument :input, required: true,
39
+ desc: 'String input'
40
+ option :light, default: false, values: %w[true false],
41
+ desc: 'true = light display (displays only the spoof payload for easy piping with other ' \
42
+ 'commands), false = full display'
43
+ option :prefix, default: nil, desc: 'Prefix Bidi. Default: RLO (U+202E).'
44
+ option :suffix, default: nil, desc: 'Suffix Bidi. Default: PDF (U+202C).'
45
+ option :infix_bidi, default: nil, desc: 'Bidi injected at a chosen position. Default: none (empty string).'
46
+ option :infix_pos, default: nil, desc: 'Spoof payload (input string with injected BiDi)'
47
+
48
+ # Craft a payload for BiDi attacks
49
+ # @param input [String] Input string to spoof
50
+ # @param options [Hash] optional parameters, see {Unisec::Bidi::Spoof.bidi_affix}
51
+ def call(input: nil, **options)
52
+ to_bool = ->(str) { ['true', true].include?(str) }
53
+ light = to_bool.call(options.fetch(:light))
54
+ infix_pos = options[:infix_pos].to_i unless options[:infix_pos].nil?
55
+ puts Unisec::Bidi::Spoof.new(input, prefix: options[:prefix], suffix: options[:suffix],
56
+ infix_bidi: options[:infix_bidi],
57
+ infix_pos: infix_pos).display(light: light)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'unisec/cli/bidi'
3
4
  require 'unisec/cli/confusables'
4
5
  require 'unisec/cli/hexdump'
5
6
  require 'unisec/cli/properties'
@@ -17,6 +18,7 @@ module Unisec
17
18
 
18
19
  # Mapping between the (sub-)commands as seen by the user
19
20
  # on the command-line interface and the CLI modules in the lib
21
+ register 'bidi spoof', Bidi::Spoof
20
22
  register 'confusables list', Confusables::List
21
23
  register 'confusables randomize', Confusables::Randomize
22
24
  register 'grep', Grep
@@ -22,12 +22,21 @@ module Unisec
22
22
  desc 'Hexdump in all Unicode encodings'
23
23
 
24
24
  argument :input, required: true,
25
- desc: 'String input'
25
+ desc: 'String input. Read from STDIN if equal to -.'
26
+
27
+ option :enc, default: nil, values: %w[utf8 utf16be utf16le utf32be utf32le],
28
+ desc: 'Output only in the specified encoding.'
26
29
 
27
30
  # Hexdump of all Unicode encodings.
28
31
  # @param input [String] Input string to encode
29
- def call(input: nil, **)
30
- puts Unisec::Hexdump.new(input).display
32
+ def call(input: nil, **options)
33
+ input = $stdin.read.chomp if input == '-'
34
+ if options[:enc].nil?
35
+ puts Unisec::Hexdump.new(input).display
36
+ else
37
+ # using send() is safe here thanks to the value whitelist
38
+ puts puts Unisec::Hexdump.send(options[:enc], input)
39
+ end
31
40
  end
32
41
  end
33
42
  end
data/lib/unisec/utils.rb CHANGED
@@ -98,6 +98,16 @@ module Unisec
98
98
  :string
99
99
  end
100
100
  end
101
+
102
+ # Reverse a string by graphemes (not by code points)
103
+ # @return [String] the reversed string
104
+ # @example
105
+ # b = "\u{1f1eb}\u{1f1f7}\u{1F413}" # => "🇫🇷🐓"
106
+ # b.reverse # => "🐓🇷🇫"
107
+ # Unisec::Utils::String.grapheme_reverse(b) # => "🐓🇫🇷"
108
+ def self.grapheme_reverse(str)
109
+ str.grapheme_clusters.reverse.join
110
+ end
101
111
  end
102
112
  end
103
113
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Unisec
4
4
  # Version of unisec library and app
5
- VERSION = '0.0.3'
5
+ VERSION = '0.0.4'
6
6
  end
data/lib/unisec.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'unisec/version'
4
4
 
5
+ require 'unisec/bidi'
5
6
  require 'unisec/confusables'
6
7
  require 'unisec/hexdump'
7
8
  require 'unisec/properties'
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.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre ZANNI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-18 00:00:00.000000000 Z
11
+ date: 2024-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ctf-party
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.3'
19
+ version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.3'
26
+ version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: dry-cli
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -99,6 +99,8 @@ files:
99
99
  - bin/unisec
100
100
  - data/DerivedName.txt
101
101
  - lib/unisec.rb
102
+ - lib/unisec/bidi.rb
103
+ - lib/unisec/cli/bidi.rb
102
104
  - lib/unisec/cli/cli.rb
103
105
  - lib/unisec/cli/confusables.rb
104
106
  - lib/unisec/cli/hexdump.rb
@@ -145,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
147
  - !ruby/object:Gem::Version
146
148
  version: '0'
147
149
  requirements: []
148
- rubygems_version: 3.4.10
150
+ rubygems_version: 3.5.3
149
151
  signing_key:
150
152
  specification_version: 4
151
153
  summary: Unicode Security Toolkit