ronin-code-sql 2.0.0 → 2.1.1
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 +17 -1
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -1
- data/ChangeLog.md +16 -1
- data/Gemfile +4 -0
- data/README.md +22 -14
- data/Rakefile +1 -0
- data/gemspec.yml +1 -1
- data/lib/ronin/code/sql/binary_expr.rb +37 -4
- data/lib/ronin/code/sql/clause.rb +25 -14
- data/lib/ronin/code/sql/clauses.rb +17 -15
- data/lib/ronin/code/sql/emittable.rb +10 -3
- data/lib/ronin/code/sql/emitter.rb +69 -26
- data/lib/ronin/code/sql/field.rb +32 -7
- data/lib/ronin/code/sql/fields.rb +2 -2
- data/lib/ronin/code/sql/function.rb +17 -6
- data/lib/ronin/code/sql/functions.rb +4 -16
- data/lib/ronin/code/sql/injection.rb +8 -8
- data/lib/ronin/code/sql/injection_expr.rb +7 -7
- data/lib/ronin/code/sql/literal.rb +19 -4
- data/lib/ronin/code/sql/literals.rb +2 -2
- data/lib/ronin/code/sql/mixin.rb +95 -0
- data/lib/ronin/code/sql/operators.rb +1 -1
- data/lib/ronin/code/sql/statement.rb +19 -8
- data/lib/ronin/code/sql/statement_list.rb +9 -9
- data/lib/ronin/code/sql/statements.rb +1 -1
- data/lib/ronin/code/sql/unary_expr.rb +25 -3
- data/lib/ronin/code/sql/version.rb +3 -3
- data/lib/ronin/code/sql.rb +5 -64
- data/lib/ronin/code/sqli.rb +30 -0
- data/ronin-code-sql.gemspec +3 -4
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0165dc3946e79bbbe0485338ff3c428f79f5eb28e35efc20fab1d2a0c92eb0d6
|
4
|
+
data.tar.gz: 6fc74345c7179f833eec309052ff8c8cccacbf5e98ac29267e50443f3dd2af4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7bd8f7552183364ab26f237413935ca10e0772acf2d3a62b4634ada80de19a02532c22ee9aa9a41e764d4c4ccc522ce1e66dad1d7fb4e6df77879516d93ec2e8
|
7
|
+
data.tar.gz: bb5767cac1ba1fe4ae7fc68b30477f0e6e862c0fc2831fd142124eeabb1d2038f4c00148c3ac3ec599d257c9e247ca9b7248903e71e79ce1bc11d5bde2a9bc6b
|
data/.github/workflows/ruby.yml
CHANGED
@@ -12,11 +12,13 @@ jobs:
|
|
12
12
|
- '3.0'
|
13
13
|
- '3.1'
|
14
14
|
- '3.2'
|
15
|
+
- '3.3'
|
16
|
+
- '3.4'
|
15
17
|
- jruby
|
16
18
|
- truffleruby
|
17
19
|
name: Ruby ${{ matrix.ruby }}
|
18
20
|
steps:
|
19
|
-
- uses: actions/checkout@
|
21
|
+
- uses: actions/checkout@v4
|
20
22
|
- name: Set up Ruby
|
21
23
|
uses: ruby/setup-ruby@v1
|
22
24
|
with:
|
@@ -26,3 +28,17 @@ jobs:
|
|
26
28
|
run: bundle install --jobs 4 --retry 3
|
27
29
|
- name: Run tests
|
28
30
|
run: bundle exec rake test
|
31
|
+
|
32
|
+
# rubocop linting
|
33
|
+
rubocop:
|
34
|
+
runs-on: ubuntu-latest
|
35
|
+
steps:
|
36
|
+
- uses: actions/checkout@v4
|
37
|
+
- name: Set up Ruby
|
38
|
+
uses: ruby/setup-ruby@v1
|
39
|
+
with:
|
40
|
+
ruby-version: 3.0
|
41
|
+
- name: Install dependencies
|
42
|
+
run: bundle install --jobs 4 --retry 3
|
43
|
+
- name: Run rubocop
|
44
|
+
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/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-3.
|
1
|
+
ruby-3.3
|
data/ChangeLog.md
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
-
### 2.
|
1
|
+
### 2.1.1 / 2025-02-14
|
2
|
+
|
3
|
+
* Omit parentheses when formatting SQL lists containing only one element
|
4
|
+
(ex: `order_by(1)` -> `ORDER BY 1`, not `ORDER BY (1)`).
|
5
|
+
* Use `require_relative` to improve load times.
|
6
|
+
|
7
|
+
### 2.1.0 / 2023-06-26
|
8
|
+
|
9
|
+
* Added {Ronin::Code::SQL::Mixin}.
|
10
|
+
* Added {Ronin::Code::SQLI} as an alias for {Ronin::Code::SQL::Injection}.
|
11
|
+
* Added support for the `syntax:` and `comment:` keyword arguments to
|
12
|
+
{Ronin::Code::SQL::Statement#to_sql} and {Ronin::Code::SQL::Injection#to_sql}.
|
13
|
+
* Added {Ronin::Code::SQL::Clauses#order_by}.
|
14
|
+
* Added {Ronin::Code::SQL::Emitter#emit_comment}.
|
15
|
+
|
16
|
+
### 2.0.0 / 2023-02-01
|
2
17
|
|
3
18
|
* Require `ruby` >= 3.0.0.
|
4
19
|
* 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
@@ -8,7 +8,6 @@
|
|
8
8
|
* [Issues](https://github.com/ronin-rb/ronin-code-sql/issues)
|
9
9
|
* [Documentation](https://ronin-rb.dev/docs/ronin-code-sql/frames)
|
10
10
|
* [Discord](https://discord.gg/6WAb3PsVX9) |
|
11
|
-
[Twitter](https://twitter.com/ronin_rb) |
|
12
11
|
[Mastodon](https://infosec.exchange/@ronin_rb)
|
13
12
|
|
14
13
|
## Description
|
@@ -61,7 +60,7 @@ string.sql_decode
|
|
61
60
|
Injecting a `1=1` test into a Integer comparison:
|
62
61
|
|
63
62
|
```ruby
|
64
|
-
sqli = Ronin::Code::
|
63
|
+
sqli = Ronin::Code::SQLI.new
|
65
64
|
sqli.or { 1 == 1 }
|
66
65
|
puts sqli
|
67
66
|
# 1 OR 1=1
|
@@ -70,7 +69,7 @@ puts sqli
|
|
70
69
|
Injecting a `1=1` test into a String comparison:
|
71
70
|
|
72
71
|
```ruby
|
73
|
-
sqli = Ronin::Code::
|
72
|
+
sqli = Ronin::Code::SQLI.new(escape: :string)
|
74
73
|
sqli.or { string(1) == string(1) }
|
75
74
|
puts sqli
|
76
75
|
# 1' OR '1'='1
|
@@ -79,7 +78,7 @@ puts sqli
|
|
79
78
|
Columns:
|
80
79
|
|
81
80
|
```ruby
|
82
|
-
sqli = Ronin::Code::
|
81
|
+
sqli = Ronin::Code::SQLI.new
|
83
82
|
sqli.and { admin == 1 }
|
84
83
|
puts sqli
|
85
84
|
# 1 AND admin=1
|
@@ -88,7 +87,7 @@ puts sqli
|
|
88
87
|
Clauses:
|
89
88
|
|
90
89
|
```ruby
|
91
|
-
sqli = Ronin::Code::
|
90
|
+
sqli = Ronin::Code::SQLI.new
|
92
91
|
sqli.or { 1 == 1 }.limit(0)
|
93
92
|
puts sqli
|
94
93
|
# 1 OR 1=1 LIMIT 0
|
@@ -97,7 +96,7 @@ puts sqli
|
|
97
96
|
Statements:
|
98
97
|
|
99
98
|
```ruby
|
100
|
-
sqli = Ronin::Code::
|
99
|
+
sqli = Ronin::Code::SQLI.new
|
101
100
|
sqli.and { 1 == 0 }
|
102
101
|
sqli.insert.into(:users).values('hacker','passw0rd','t')
|
103
102
|
puts sqli
|
@@ -107,7 +106,7 @@ puts sqli
|
|
107
106
|
Sub-Statements:
|
108
107
|
|
109
108
|
```ruby
|
110
|
-
sqli = Ronin::Code::
|
109
|
+
sqli = Ronin::Code::SQLI.new
|
111
110
|
sqli.union { select(1,2,3,4,id).from(users) }
|
112
111
|
puts sqli
|
113
112
|
# 1 UNION SELECT (1,2,3,4,id) FROM users
|
@@ -116,7 +115,7 @@ puts sqli
|
|
116
115
|
Test if a table exists:
|
117
116
|
|
118
117
|
```ruby
|
119
|
-
sqli = Ronin::Code::
|
118
|
+
sqli = Ronin::Code::SQLI.new
|
120
119
|
sqli.and { select(count).from(:users) == 1 }
|
121
120
|
puts sqli
|
122
121
|
# 1 AND (SELECT COUNT(*) FROM users)=1
|
@@ -125,7 +124,7 @@ puts sqli
|
|
125
124
|
Create errors by using non-existent tables:
|
126
125
|
|
127
126
|
```ruby
|
128
|
-
sqli = Ronin::Code::
|
127
|
+
sqli = Ronin::Code::SQLI.new(escape: :string)
|
129
128
|
sqli.and { non_existent_table == '1' }
|
130
129
|
puts sqli
|
131
130
|
# 1' AND non_existent_table='1
|
@@ -134,7 +133,7 @@ puts sqli
|
|
134
133
|
Dumping all values of a column:
|
135
134
|
|
136
135
|
```ruby
|
137
|
-
sqli = Ronin::Code::
|
136
|
+
sqli = Ronin::Code::SQLI.new(escape: :string)
|
138
137
|
sqli.or { username.is_not(null) }.or { username == '' }
|
139
138
|
puts sqli
|
140
139
|
# 1' OR username IS NOT NULL OR username='
|
@@ -143,7 +142,7 @@ puts sqli
|
|
143
142
|
Enumerate through database table names:
|
144
143
|
|
145
144
|
```ruby
|
146
|
-
sqli = Ronin::Code::
|
145
|
+
sqli = Ronin::Code::SQLI.new
|
147
146
|
sqli.and {
|
148
147
|
ascii(
|
149
148
|
lower(
|
@@ -160,7 +159,7 @@ puts sqli
|
|
160
159
|
Find user supplied tables via the `sysObjects` table:
|
161
160
|
|
162
161
|
```ruby
|
163
|
-
sqli = Ronin::Code::
|
162
|
+
sqli = Ronin::Code::SQLI.new
|
164
163
|
sqli.union_all {
|
165
164
|
select(1,2,3,4,5,6,name).from(sysObjects).where { xtype == 'U' }
|
166
165
|
}
|
@@ -171,12 +170,21 @@ puts sqli.to_sql(terminate: true)
|
|
171
170
|
Bypass filters using `/**/` instead of spaces:
|
172
171
|
|
173
172
|
```ruby
|
174
|
-
sqli = Ronin::Code::
|
173
|
+
sqli = Ronin::Code::SQLI.new
|
175
174
|
sqli.union { select(1,2,3,4,id).from(users) }
|
176
175
|
puts sqli.to_sql(space: '/**/')
|
177
176
|
# 1/**/UNION/**/SELECT/**/(1,2,3,4,id)/**/FROM/**/users
|
178
177
|
```
|
179
178
|
|
179
|
+
Bypass filters using MySQL `#` comments:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
sqli = Ronin::Code::SQLI.new
|
183
|
+
sqli.or { 1 == 1 }
|
184
|
+
puts sqli.to_sql(terminate: true, comment: '#')
|
185
|
+
# 1 OR 1=1 OR 1=1;#
|
186
|
+
```
|
187
|
+
|
180
188
|
## Requirements
|
181
189
|
|
182
190
|
* [Ruby] >= 3.0.0
|
@@ -192,7 +200,7 @@ $ gem install ronin-code-sql
|
|
192
200
|
|
193
201
|
ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
194
202
|
|
195
|
-
Copyright (c) 2007-
|
203
|
+
Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
|
196
204
|
|
197
205
|
ronin-code-sql is free software: you can redistribute it and/or modify
|
198
206
|
it under the terms of the GNU Lesser General Public License as published
|
data/Rakefile
CHANGED
data/gemspec.yml
CHANGED
@@ -3,7 +3,7 @@ summary: A Ruby DSL for crafting SQL Injections.
|
|
3
3
|
description:
|
4
4
|
ronin-code-sql is a Ruby DSL for crafting SQL Injections.
|
5
5
|
|
6
|
-
license: LGPL-3.0
|
6
|
+
license: LGPL-3.0-or-later
|
7
7
|
authors: Postmodern
|
8
8
|
email: postmodern.mod3@gmail.com
|
9
9
|
homepage: https://github.com/ronin-rb/ronin-code-sql#readme
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2007-
|
5
|
+
# Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -18,8 +18,8 @@
|
|
18
18
|
# along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
|
19
19
|
#
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
require_relative 'operators'
|
22
|
+
require_relative 'emittable'
|
23
23
|
|
24
24
|
module Ronin
|
25
25
|
module Code
|
@@ -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
|
#
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2007-
|
5
|
+
# Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -18,12 +18,12 @@
|
|
18
18
|
# along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
|
19
19
|
#
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
require_relative 'literals'
|
22
|
+
require_relative 'fields'
|
23
|
+
require_relative 'functions'
|
24
|
+
require_relative 'statement'
|
25
|
+
require_relative 'statements'
|
26
|
+
require_relative 'emittable'
|
27
27
|
|
28
28
|
module Ronin
|
29
29
|
module Code
|
@@ -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
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2007-
|
5
|
+
# Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -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
|
#
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2007-
|
5
|
+
# Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
|
19
19
|
#
|
20
20
|
|
21
|
-
|
21
|
+
require_relative 'emitter'
|
22
22
|
|
23
23
|
module Ronin
|
24
24
|
module Code
|
@@ -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
|
#
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-code-sql - A Ruby DSL for crafting SQL Injections.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2007-
|
5
|
+
# Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-code-sql is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -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,11 @@ module Ronin
|
|
200
238
|
# The raw SQL.
|
201
239
|
#
|
202
240
|
def emit_list(list)
|
203
|
-
|
241
|
+
if list.length == 1
|
242
|
+
emit(list.first)
|
243
|
+
else
|
244
|
+
"(#{list.map { |element| emit(element) }.join(',')})"
|
245
|
+
end
|
204
246
|
end
|
205
247
|
|
206
248
|
#
|
@@ -250,7 +292,8 @@ module Ronin
|
|
250
292
|
|
251
293
|
case expr
|
252
294
|
when BinaryExpr
|
253
|
-
left
|
295
|
+
left = emit_argument(expr.left)
|
296
|
+
right = emit_argument(expr.right)
|
254
297
|
|
255
298
|
case op
|
256
299
|
when /^\W+$/ then "#{left}#{op}#{right}"
|
@@ -366,16 +409,16 @@ module Ronin
|
|
366
409
|
sql = emit_keyword(stmt.keyword)
|
367
410
|
|
368
411
|
unless stmt.argument.nil?
|
369
|
-
case stmt.argument
|
370
|
-
|
371
|
-
|
412
|
+
sql << @space << case stmt.argument
|
413
|
+
when Array
|
414
|
+
if stmt.argument.length == 1
|
372
415
|
emit_argument(stmt.argument[0])
|
373
416
|
else
|
374
417
|
emit_list(stmt.argument)
|
375
418
|
end
|
376
|
-
|
377
|
-
|
378
|
-
|
419
|
+
else
|
420
|
+
emit_argument(stmt.argument)
|
421
|
+
end
|
379
422
|
end
|
380
423
|
|
381
424
|
unless stmt.clauses.empty?
|