one_gadget 1.5.0 → 1.6.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/README.md +5 -4
- data/bin/one_gadget +11 -0
- data/lib/one_gadget/abi.rb +5 -2
- data/lib/one_gadget/builds/libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.rb +49 -0
- data/lib/one_gadget/builds/libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.rb +52 -0
- data/lib/one_gadget/builds/libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.rb +47 -0
- data/lib/one_gadget/builds/libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.rb +41 -0
- data/lib/one_gadget/emulators/instruction.rb +2 -2
- data/lib/one_gadget/emulators/lambda.rb +5 -5
- data/lib/one_gadget/emulators/processor.rb +8 -2
- data/lib/one_gadget/emulators/x86.rb +71 -16
- data/lib/one_gadget/error.rb +9 -0
- data/lib/one_gadget/fetcher.rb +1 -3
- data/lib/one_gadget/gadget.rb +27 -0
- data/lib/one_gadget/helper.rb +30 -10
- data/lib/one_gadget/logger.rb +2 -2
- data/lib/one_gadget/update.rb +2 -2
- data/lib/one_gadget/version.rb +1 -1
- metadata +16 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d381a734bccb70a6ffaac1b6e2ff82518adf2719
|
4
|
+
data.tar.gz: 406db08cea4fc116b4d81f61d585b7a25ec9ecd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 327cb7edce0f03ac5d43807d91c9850f63869e9041d59ed236b467fd6ca97aa4aaaa68361525daa1273259b7df96a00e69193ef1ddd3b4856126a61afb3148ad
|
7
|
+
data.tar.gz: 59b561675611597e011b5012574986138e041421eaecd8819ca40cdecc8f68056a2f42077eb7ad2f25f71ce2fd884b53aba9ea823791d250e18703d8e7228a3b
|
data/README.md
CHANGED
@@ -28,9 +28,9 @@ Note: require ruby version >= 2.1.0, you can use `ruby --version` to check.
|
|
28
28
|
|
29
29
|
## Implementation
|
30
30
|
|
31
|
-
OneGadget uses simple self-implement symbolic execution to find the constraints of gadgets to
|
31
|
+
OneGadget uses simple self-implement symbolic execution to find the constraints of gadgets to be successful.
|
32
32
|
|
33
|
-
The article introducing how I develop this tool can be found [
|
33
|
+
The article introducing how I develop this tool can be found [in my blog](https://david942j.blogspot.com/2017/02/project-one-gadget-in-glibc.html).
|
34
34
|
|
35
35
|
## Usage
|
36
36
|
|
@@ -39,7 +39,7 @@ much more one-gadgets have been found.
|
|
39
39
|
And gadgets become too many to show them all,
|
40
40
|
they would be selected automatically according to the difficulty of constraints.
|
41
41
|
Therefore, gadgets shown will be less than previous versions (before v1.5.0).
|
42
|
-
|
42
|
+
But you can use option `--level 1` to show all gadgets found.
|
43
43
|
|
44
44
|
### Command Line Interface
|
45
45
|
|
@@ -55,6 +55,7 @@ $ one_gadget
|
|
55
55
|
# -r, --[no-]raw Output gadgets offset only, split with one space.
|
56
56
|
# -s, --script exploit-script Run exploit script with all possible gadgets.
|
57
57
|
# The script will be run as 'exploit-script $offset'.
|
58
|
+
# --info BuildID Show version information given BuildID.
|
58
59
|
# --version Current gem version.
|
59
60
|
|
60
61
|
$ one_gadget -b 60131540dadc6796cab33388349e6e4e68692053
|
@@ -210,4 +211,4 @@ one_gadget('60131540dadc6796cab33388349e6e4e68692053')
|
|
210
211
|
Any suggestion or feature request is welcome! Feel free to send a pull request.
|
211
212
|
|
212
213
|
Please let me know if you find any libc that make OneGadget fail to find gadgets.
|
213
|
-
And, if you like this work, I'll be happy to be [
|
214
|
+
And, if you like this work, I'll be happy to be [starred](https://github.com/david942j/one_gadget/stargazers) :grimacing:
|
data/bin/one_gadget
CHANGED
@@ -31,6 +31,10 @@ parser = OptionParser.new do |opts|
|
|
31
31
|
options[:script] = script
|
32
32
|
end
|
33
33
|
|
34
|
+
opts.on('--info BuildID', 'Show version information given BuildID.') do |b|
|
35
|
+
options[:info] = b
|
36
|
+
end
|
37
|
+
|
34
38
|
opts.on('--version', 'Current gem version.') do |v|
|
35
39
|
options[:version] = v
|
36
40
|
end
|
@@ -49,6 +53,13 @@ def execute(script, offset)
|
|
49
53
|
Process.wait pid
|
50
54
|
end
|
51
55
|
|
56
|
+
if options[:info]
|
57
|
+
result = OneGadget::Gadget.builds_info(options[:info])
|
58
|
+
exit(1) if result.nil? # error happend
|
59
|
+
OneGadget::Logger.info("Information of #{options[:info]}:\n#{result.join("\n")}\n")
|
60
|
+
exit(0)
|
61
|
+
end
|
62
|
+
|
52
63
|
level = options[:level] || 0
|
53
64
|
if options[:build_id]
|
54
65
|
gadgets = OneGadget.gadgets(build_id: options[:build_id], details: true, level: level)
|
data/lib/one_gadget/abi.rb
CHANGED
@@ -4,9 +4,12 @@ module OneGadget
|
|
4
4
|
# Define class methods here.
|
5
5
|
module ClassMethods
|
6
6
|
# Registers in i386.
|
7
|
-
LINUX_X86_32 = %w(eax ebx ecx edx edi esi ebp esp).
|
7
|
+
LINUX_X86_32 = %w(eax ebx ecx edx edi esi ebp esp) + 0.upto(7).map { |i| "xmm#{i}" }
|
8
8
|
# Registers in x86_64/
|
9
|
-
LINUX_X86_64 = LINUX_X86_32 +
|
9
|
+
LINUX_X86_64 = LINUX_X86_32 +
|
10
|
+
%w(rax rbx rcx rdx rdi rsi rbp rsp) +
|
11
|
+
8.upto(15).map { |i| "r#{i}" } +
|
12
|
+
8.upto(15).map { |i| "xmm#{i}" }
|
10
13
|
# Registers' name in amd64.
|
11
14
|
# @return [Array<String>] List of registers.
|
12
15
|
def amd64
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'one_gadget/gadget'
|
2
|
+
# ubuntu17.10-libc-2.26.so
|
3
|
+
#
|
4
|
+
# Advanced Micro Devices X86-64
|
5
|
+
#
|
6
|
+
# GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2.1) stable release version 2.26, by Roland McGrath et al.
|
7
|
+
# Copyright (C) 2017 Free Software Foundation, Inc.
|
8
|
+
# This is free software; see the source for copying conditions.
|
9
|
+
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
|
10
|
+
# PARTICULAR PURPOSE.
|
11
|
+
# Compiled by GNU CC version 6.4.0 20171010.
|
12
|
+
# Available extensions:
|
13
|
+
# crypt add-on version 2.1 by Michael Glad and others
|
14
|
+
# GNU Libidn by Simon Josefsson
|
15
|
+
# Native POSIX Threads Library by Ulrich Drepper et al
|
16
|
+
# BIND-8.2.3-T5B
|
17
|
+
# libc ABIs: UNIQUE IFUNC
|
18
|
+
# For bug reporting instructions, please see:
|
19
|
+
# <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
|
20
|
+
|
21
|
+
build_id = File.basename(__FILE__, '.rb').split('-').last
|
22
|
+
OneGadget::Gadget.add(build_id, 293958,
|
23
|
+
constraints: ["rax == NULL"],
|
24
|
+
effect: "execve(\"/bin/sh\", rsp+0x30, environ)")
|
25
|
+
OneGadget::Gadget.add(build_id, 294042,
|
26
|
+
constraints: ["[rsp+0x30] == NULL"],
|
27
|
+
effect: "execve(\"/bin/sh\", rsp+0x30, environ)")
|
28
|
+
OneGadget::Gadget.add(build_id, 890723,
|
29
|
+
constraints: ["[r13] == NULL || r13 == NULL", "[rbx] == NULL || rbx == NULL"],
|
30
|
+
effect: "execve(\"/bin/sh\", r13, rbx)")
|
31
|
+
OneGadget::Gadget.add(build_id, 891441,
|
32
|
+
constraints: ["[[rbp-0xa0]] == NULL || [rbp-0xa0] == NULL", "[[rbp-0x70]] == NULL || [rbp-0x70] == NULL"],
|
33
|
+
effect: "execve(\"/bin/sh\", [rbp-0xa0], [rbp-0x70])")
|
34
|
+
OneGadget::Gadget.add(build_id, 891448,
|
35
|
+
constraints: ["[rcx] == NULL || rcx == NULL", "[[rbp-0x70]] == NULL || [rbp-0x70] == NULL"],
|
36
|
+
effect: "execve(\"/bin/sh\", rcx, [rbp-0x70])")
|
37
|
+
OneGadget::Gadget.add(build_id, 891452,
|
38
|
+
constraints: ["[rcx] == NULL || rcx == NULL", "[rdx] == NULL || rdx == NULL"],
|
39
|
+
effect: "execve(\"/bin/sh\", rcx, rdx)")
|
40
|
+
OneGadget::Gadget.add(build_id, 1035486,
|
41
|
+
constraints: ["[rsp+0x40] == NULL"],
|
42
|
+
effect: "execve(\"/bin/sh\", rsp+0x40, environ)")
|
43
|
+
OneGadget::Gadget.add(build_id, 1035498,
|
44
|
+
constraints: ["[rsi] == NULL || rsi == NULL", "[[rax]] == NULL || [rax] == NULL"],
|
45
|
+
effect: "execve(\"/bin/sh\", rsi, [rax])")
|
46
|
+
OneGadget::Gadget.add(build_id, 1039246,
|
47
|
+
constraints: ["[rsp+0x70] == NULL"],
|
48
|
+
effect: "execve(\"/bin/sh\", rsp+0x70, environ)")
|
49
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'one_gadget/gadget'
|
2
|
+
# ubuntu17.10-libc-2.26.so
|
3
|
+
#
|
4
|
+
# Intel 80386
|
5
|
+
#
|
6
|
+
# GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2.1) stable release version 2.26, by Roland McGrath et al.
|
7
|
+
# Copyright (C) 2017 Free Software Foundation, Inc.
|
8
|
+
# This is free software; see the source for copying conditions.
|
9
|
+
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
|
10
|
+
# PARTICULAR PURPOSE.
|
11
|
+
# Compiled by GNU CC version 6.4.0 20171010.
|
12
|
+
# Available extensions:
|
13
|
+
# crypt add-on version 2.1 by Michael Glad and others
|
14
|
+
# GNU Libidn by Simon Josefsson
|
15
|
+
# Native POSIX Threads Library by Ulrich Drepper et al
|
16
|
+
# BIND-8.2.3-T5B
|
17
|
+
# libc ABIs: UNIQUE IFUNC
|
18
|
+
# For bug reporting instructions, please see:
|
19
|
+
# <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
|
20
|
+
|
21
|
+
build_id = File.basename(__FILE__, '.rb').split('-').last
|
22
|
+
OneGadget::Gadget.add(build_id, 248879,
|
23
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x34] == NULL"],
|
24
|
+
effect: "execve(\"/bin/sh\", esp+0x34, environ)")
|
25
|
+
OneGadget::Gadget.add(build_id, 248881,
|
26
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x38] == NULL"],
|
27
|
+
effect: "execve(\"/bin/sh\", esp+0x38, environ)")
|
28
|
+
OneGadget::Gadget.add(build_id, 248885,
|
29
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x3c] == NULL"],
|
30
|
+
effect: "execve(\"/bin/sh\", esp+0x3c, environ)")
|
31
|
+
OneGadget::Gadget.add(build_id, 248892,
|
32
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x40] == NULL"],
|
33
|
+
effect: "execve(\"/bin/sh\", esp+0x40, environ)")
|
34
|
+
OneGadget::Gadget.add(build_id, 248927,
|
35
|
+
constraints: ["esi is the GOT address of libc", "[eax] == NULL || eax == NULL", "[[esp]] == NULL || [esp] == NULL"],
|
36
|
+
effect: "execve(\"/bin/sh\", eax, [esp])")
|
37
|
+
OneGadget::Gadget.add(build_id, 248928,
|
38
|
+
constraints: ["esi is the GOT address of libc", "[[esp]] == NULL || [esp] == NULL", "[[esp+0x4]] == NULL || [esp+0x4] == NULL"],
|
39
|
+
effect: "execve(\"/bin/sh\", [esp], [esp+0x4])")
|
40
|
+
OneGadget::Gadget.add(build_id, 421503,
|
41
|
+
constraints: ["edi is the GOT address of libc", "eax == NULL"],
|
42
|
+
effect: "execl(\"/bin/sh\", eax)")
|
43
|
+
OneGadget::Gadget.add(build_id, 421504,
|
44
|
+
constraints: ["edi is the GOT address of libc", "[esp] == NULL"],
|
45
|
+
effect: "execl(\"/bin/sh\", [esp])")
|
46
|
+
OneGadget::Gadget.add(build_id, 1257406,
|
47
|
+
constraints: ["esi is the GOT address of libc", "eax == NULL"],
|
48
|
+
effect: "execl(\"/bin/sh\", eax)")
|
49
|
+
OneGadget::Gadget.add(build_id, 1257407,
|
50
|
+
constraints: ["esi is the GOT address of libc", "[esp] == NULL"],
|
51
|
+
effect: "execl(\"/bin/sh\", [esp])")
|
52
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'one_gadget/gadget'
|
2
|
+
# spec/data/libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.so
|
3
|
+
#
|
4
|
+
# Intel 80386
|
5
|
+
#
|
6
|
+
# GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
|
7
|
+
# Copyright (C) 2018 Free Software Foundation, Inc.
|
8
|
+
# This is free software; see the source for copying conditions.
|
9
|
+
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
|
10
|
+
# PARTICULAR PURPOSE.
|
11
|
+
# Compiled by GNU CC version 7.3.0.
|
12
|
+
# libc ABIs: UNIQUE IFUNC
|
13
|
+
# For bug reporting instructions, please see:
|
14
|
+
# <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
|
15
|
+
|
16
|
+
build_id = File.basename(__FILE__, '.rb').split('-').last
|
17
|
+
OneGadget::Gadget.add(build_id, 248810,
|
18
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x34] == NULL"],
|
19
|
+
effect: "execve(\"/bin/sh\", esp+0x34, environ)")
|
20
|
+
OneGadget::Gadget.add(build_id, 248812,
|
21
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x38] == NULL"],
|
22
|
+
effect: "execve(\"/bin/sh\", esp+0x38, environ)")
|
23
|
+
OneGadget::Gadget.add(build_id, 248816,
|
24
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x3c] == NULL"],
|
25
|
+
effect: "execve(\"/bin/sh\", esp+0x3c, environ)")
|
26
|
+
OneGadget::Gadget.add(build_id, 248823,
|
27
|
+
constraints: ["esi is the GOT address of libc", "[esp+0x40] == NULL"],
|
28
|
+
effect: "execve(\"/bin/sh\", esp+0x40, environ)")
|
29
|
+
OneGadget::Gadget.add(build_id, 248858,
|
30
|
+
constraints: ["esi is the GOT address of libc", "[eax] == NULL || eax == NULL", "[[esp]] == NULL || [esp] == NULL"],
|
31
|
+
effect: "execve(\"/bin/sh\", eax, [esp])")
|
32
|
+
OneGadget::Gadget.add(build_id, 248859,
|
33
|
+
constraints: ["esi is the GOT address of libc", "[[esp]] == NULL || [esp] == NULL", "[[esp+0x4]] == NULL || [esp+0x4] == NULL"],
|
34
|
+
effect: "execve(\"/bin/sh\", [esp], [esp+0x4])")
|
35
|
+
OneGadget::Gadget.add(build_id, 422559,
|
36
|
+
constraints: ["esi is the GOT address of libc", "eax == NULL"],
|
37
|
+
effect: "execl(\"/bin/sh\", eax)")
|
38
|
+
OneGadget::Gadget.add(build_id, 422560,
|
39
|
+
constraints: ["esi is the GOT address of libc", "[esp] == NULL"],
|
40
|
+
effect: "execl(\"/bin/sh\", [esp])")
|
41
|
+
OneGadget::Gadget.add(build_id, 1267518,
|
42
|
+
constraints: ["ebx is the GOT address of libc", "eax == NULL"],
|
43
|
+
effect: "execl(\"/bin/sh\", eax)")
|
44
|
+
OneGadget::Gadget.add(build_id, 1267519,
|
45
|
+
constraints: ["ebx is the GOT address of libc", "[esp] == NULL"],
|
46
|
+
effect: "execl(\"/bin/sh\", [esp])")
|
47
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'one_gadget/gadget'
|
2
|
+
# spec/data/libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.so
|
3
|
+
#
|
4
|
+
# Advanced Micro Devices X86-64
|
5
|
+
#
|
6
|
+
# GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
|
7
|
+
# Copyright (C) 2018 Free Software Foundation, Inc.
|
8
|
+
# This is free software; see the source for copying conditions.
|
9
|
+
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
|
10
|
+
# PARTICULAR PURPOSE.
|
11
|
+
# Compiled by GNU CC version 7.3.0.
|
12
|
+
# libc ABIs: UNIQUE IFUNC
|
13
|
+
# For bug reporting instructions, please see:
|
14
|
+
# <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
|
15
|
+
|
16
|
+
build_id = File.basename(__FILE__, '.rb').split('-').last
|
17
|
+
OneGadget::Gadget.add(build_id, 324293,
|
18
|
+
constraints: ["rcx == NULL"],
|
19
|
+
effect: "execve(\"/bin/sh\", rsp+0x40, environ)")
|
20
|
+
OneGadget::Gadget.add(build_id, 324386,
|
21
|
+
constraints: ["[rsp+0x40] == NULL"],
|
22
|
+
effect: "execve(\"/bin/sh\", rsp+0x40, environ)")
|
23
|
+
OneGadget::Gadget.add(build_id, 939679,
|
24
|
+
constraints: ["[r14] == NULL || r14 == NULL", "[r12] == NULL || r12 == NULL"],
|
25
|
+
effect: "execve(\"/bin/sh\", r14, r12)")
|
26
|
+
OneGadget::Gadget.add(build_id, 940120,
|
27
|
+
constraints: ["[[rbp-0x88]] == NULL || [rbp-0x88] == NULL", "[[rbp-0x70]] == NULL || [rbp-0x70] == NULL"],
|
28
|
+
effect: "execve(\"/bin/sh\", [rbp-0x88], [rbp-0x70])")
|
29
|
+
OneGadget::Gadget.add(build_id, 940127,
|
30
|
+
constraints: ["[r10] == NULL || r10 == NULL", "[[rbp-0x70]] == NULL || [rbp-0x70] == NULL"],
|
31
|
+
effect: "execve(\"/bin/sh\", r10, [rbp-0x70])")
|
32
|
+
OneGadget::Gadget.add(build_id, 940131,
|
33
|
+
constraints: ["[r10] == NULL || r10 == NULL", "[rdx] == NULL || rdx == NULL"],
|
34
|
+
effect: "execve(\"/bin/sh\", r10, rdx)")
|
35
|
+
OneGadget::Gadget.add(build_id, 1090444,
|
36
|
+
constraints: ["[rsp+0x70] == NULL"],
|
37
|
+
effect: "execve(\"/bin/sh\", rsp+0x70, environ)")
|
38
|
+
OneGadget::Gadget.add(build_id, 1090456,
|
39
|
+
constraints: ["[rsi] == NULL || rsi == NULL", "[[rax]] == NULL || [rax] == NULL"],
|
40
|
+
effect: "execve(\"/bin/sh\", rsi, [rax])")
|
41
|
+
|
@@ -4,7 +4,7 @@ module OneGadget
|
|
4
4
|
class Instruction
|
5
5
|
attr_reader :inst # @return [String] The instruction name.
|
6
6
|
attr_reader :argc # @return [Integer] Count of arguments.
|
7
|
-
# Instantiate a
|
7
|
+
# Instantiate a {Instruction} object.
|
8
8
|
# @param [String] inst The instruction name.
|
9
9
|
# @param [Integer] argc
|
10
10
|
# Count of arguments.
|
@@ -23,7 +23,7 @@ module OneGadget
|
|
23
23
|
args = cmd[idx + inst.size..-1].split(',')
|
24
24
|
raise ArgumentError, "Incorrect argument number in #{cmd}, expect: #{argc}" if argc >= 0 && args.size != argc
|
25
25
|
args.map do |arg|
|
26
|
-
arg.gsub(/QWORD|DWORD|WORD|BYTE|PTR/, '').strip
|
26
|
+
arg.gsub(/XMMWORD|QWORD|DWORD|WORD|BYTE|PTR/, '').strip
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -95,9 +95,9 @@ module OneGadget
|
|
95
95
|
# If +arg+ contains number only, return it.
|
96
96
|
# Otherwise, return a {Lambda} object.
|
97
97
|
# @example
|
98
|
-
# obj = parse('[rsp+0x50]')
|
98
|
+
# obj = Lambda.parse('[rsp+0x50]')
|
99
99
|
# #=> #<Lambda @obj='rsp', @immi=80, @deref_count=1>
|
100
|
-
# parse('obj+0x30', predefined: { 'obj' => obj }).to_s
|
100
|
+
# Lambda.parse('obj+0x30', predefined: { 'obj' => obj }).to_s
|
101
101
|
# #=> '[rsp+0x50]+0x30'
|
102
102
|
def parse(arg, predefined: {})
|
103
103
|
deref_count = 0
|
@@ -110,10 +110,10 @@ module OneGadget
|
|
110
110
|
val = 0
|
111
111
|
if sign
|
112
112
|
raise ArgumentError, "Not support #{arg}" unless OneGadget::Helper.integer?(arg[sign..-1])
|
113
|
-
val = Integer(arg
|
114
|
-
arg = arg[0, sign]
|
113
|
+
val = Integer(arg.slice!(sign..-1))
|
115
114
|
end
|
116
|
-
obj =
|
115
|
+
obj = predefined[arg] || Lambda.new(arg)
|
116
|
+
obj += val unless val.zero?
|
117
117
|
deref_count.zero? ? obj : obj.deref
|
118
118
|
end
|
119
119
|
end
|
@@ -10,13 +10,13 @@ module OneGadget
|
|
10
10
|
# Instantiate a {Processor} object.
|
11
11
|
# @param [Array<String>] registers Registers that supported in the architecture.
|
12
12
|
def initialize(registers)
|
13
|
-
@registers = registers.map { |reg| [reg,
|
13
|
+
@registers = registers.map { |reg| [reg, to_lambda(reg)] }.to_h
|
14
14
|
@stack = {}
|
15
15
|
end
|
16
16
|
|
17
17
|
# Parse one command into instruction and arguments.
|
18
18
|
# @param [String] cmd One line of result of objdump.
|
19
|
-
# @return [
|
19
|
+
# @return [(Instruction, Array<String>)]
|
20
20
|
# The parsing result.
|
21
21
|
def parse(cmd)
|
22
22
|
inst = instructions.find { |i| i.match?(cmd) }
|
@@ -33,6 +33,12 @@ module OneGadget
|
|
33
33
|
# @return [Array<Instruction>] The support instructions.
|
34
34
|
def instructions; raise NotImplementedError
|
35
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def to_lambda(reg)
|
40
|
+
OneGadget::Emulators::Lambda.new(reg)
|
41
|
+
end
|
36
42
|
end
|
37
43
|
end
|
38
44
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
require 'one_gadget/emulators/processor'
|
2
1
|
require 'one_gadget/emulators/instruction'
|
2
|
+
require 'one_gadget/emulators/lambda'
|
3
|
+
require 'one_gadget/emulators/processor'
|
4
|
+
require 'one_gadget/error'
|
3
5
|
|
4
6
|
module OneGadget
|
5
7
|
module Emulators
|
@@ -13,10 +15,10 @@ module OneGadget
|
|
13
15
|
@sp = sp
|
14
16
|
@pc = pc
|
15
17
|
@stack = Hash.new do |h, k|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
h[k] = OneGadget::Emulators::Lambda.new(sp).tap do |lmda|
|
19
|
+
lmda.immi = k
|
20
|
+
lmda.deref!
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -24,22 +26,22 @@ module OneGadget
|
|
24
26
|
# Will raise exceptions when encounter unhandled instruction.
|
25
27
|
# @param [String] cmd
|
26
28
|
# One line from result of objdump.
|
27
|
-
# @return [
|
29
|
+
# @return [Boolean]
|
28
30
|
def process!(cmd)
|
29
31
|
inst, args = parse(cmd)
|
30
32
|
# return registers[pc] = args[0] if inst.inst == 'call'
|
31
33
|
return true if inst.inst == 'jmp' # believe the fetcher has handled jmp.
|
32
34
|
sym = "inst_#{inst.inst}".to_sym
|
33
|
-
|
35
|
+
__send__(sym, *args) != :fail
|
34
36
|
end
|
35
37
|
|
36
38
|
# Process one command, without raising any exceptions.
|
37
39
|
# @param [String] cmd
|
38
40
|
# See {#process!} for more information.
|
39
|
-
# @return [
|
41
|
+
# @return [Boolean]
|
40
42
|
def process(cmd)
|
41
43
|
process!(cmd)
|
42
|
-
rescue ArgumentError
|
44
|
+
rescue ArgumentError, OneGadget::Error::Error
|
43
45
|
false
|
44
46
|
end
|
45
47
|
|
@@ -55,7 +57,10 @@ module OneGadget
|
|
55
57
|
Instruction.new('nop', -1),
|
56
58
|
Instruction.new('push', 1),
|
57
59
|
Instruction.new('sub', 2),
|
58
|
-
Instruction.new('xor', 2)
|
60
|
+
Instruction.new('xor', 2),
|
61
|
+
Instruction.new('movq', 2),
|
62
|
+
Instruction.new('movaps', 2),
|
63
|
+
Instruction.new('movhps', 2)
|
59
64
|
]
|
60
65
|
end
|
61
66
|
|
@@ -83,12 +88,53 @@ module OneGadget
|
|
83
88
|
# Just ignore strange case...
|
84
89
|
return unless tar.include?(sp)
|
85
90
|
tar = OneGadget::Emulators::Lambda.parse(tar, predefined: registers)
|
86
|
-
return if tar.deref_count != 1 # should not
|
91
|
+
return if tar.deref_count != 1 # should not happen
|
87
92
|
tar.ref!
|
88
93
|
stack[tar.evaluate(eval_dict)] = src
|
89
94
|
end
|
90
95
|
end
|
91
96
|
|
97
|
+
# This instruction moves 128bits.
|
98
|
+
def inst_movaps(tar, src)
|
99
|
+
# XXX: here we only support `movaps [sp+*], xmm*`
|
100
|
+
# TODO: This need an extra constraint: sp & 0xf == 0
|
101
|
+
src, tar = check_xmm_sp(src, tar) { raise_unsupported('movaps', tar, src) }
|
102
|
+
off = tar.evaluate(eval_dict)
|
103
|
+
(128 / self.class.bits).times do |i|
|
104
|
+
stack[off + i * size_t] = src[i]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Mov *src to tar[:64]
|
109
|
+
def inst_movq(tar, src)
|
110
|
+
# XXX: here we only support `movq xmm*, [sp+*]`
|
111
|
+
tar, src = check_xmm_sp(tar, src) { raise_unsupported('movq', tar, src) }
|
112
|
+
off = src.evaluate(eval_dict)
|
113
|
+
(64 / self.class.bits).times do |i|
|
114
|
+
tar[i] = stack[off + i * size_t]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Move *src to tar[64:128]
|
119
|
+
def inst_movhps(tar, src)
|
120
|
+
# XXX: here we only support `movhps xmm*, [sp+*]`
|
121
|
+
tar, src = check_xmm_sp(tar, src) { raise_unsupported('movhps', tar, src) }
|
122
|
+
off = src.evaluate(eval_dict)
|
123
|
+
(64 / self.class.bits).times do |i|
|
124
|
+
tar[i + 64 / self.class.bits] = stack[off + i * size_t]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# check if (tar, src) in form (xmm*, [sp+*])
|
129
|
+
def check_xmm_sp(tar, src)
|
130
|
+
return yield unless tar.start_with?('xmm') && register?(tar) && src.include?(sp)
|
131
|
+
tar_lm = OneGadget::Emulators::Lambda.parse(tar, predefined: registers)
|
132
|
+
src_lm = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
|
133
|
+
return yield if src_lm.deref_count != 1
|
134
|
+
src_lm.ref!
|
135
|
+
[tar_lm, src_lm]
|
136
|
+
end
|
137
|
+
|
92
138
|
def inst_lea(tar, src)
|
93
139
|
src = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
|
94
140
|
src.ref!
|
@@ -97,7 +143,7 @@ module OneGadget
|
|
97
143
|
|
98
144
|
def inst_push(val)
|
99
145
|
val = OneGadget::Emulators::Lambda.parse(val, predefined: registers)
|
100
|
-
registers[sp] -=
|
146
|
+
registers[sp] -= size_t
|
101
147
|
cur_top = registers[sp].evaluate(eval_dict)
|
102
148
|
raise ArgumentError, "Corrupted stack pointer: #{cur_top}" unless cur_top.is_a?(Integer)
|
103
149
|
stack[cur_top] = val
|
@@ -150,14 +196,23 @@ module OneGadget
|
|
150
196
|
:fail
|
151
197
|
end
|
152
198
|
|
153
|
-
def
|
199
|
+
def size_t
|
154
200
|
self.class.bits / 8
|
155
201
|
end
|
156
202
|
|
157
203
|
def eval_dict
|
158
|
-
|
159
|
-
|
160
|
-
|
204
|
+
{ sp => 0 }
|
205
|
+
end
|
206
|
+
|
207
|
+
def raise_unsupported(inst, *args)
|
208
|
+
raise OneGadget::Error::UnsupportedInstructionArguments, "#{inst} #{args.join(', ')}"
|
209
|
+
end
|
210
|
+
|
211
|
+
def to_lambda(reg)
|
212
|
+
return super unless reg =~ /^xmm\d+$/
|
213
|
+
Array.new(128 / self.class.bits) do |i|
|
214
|
+
OneGadget::Emulators::Lambda.new("#{reg}__#{i}")
|
215
|
+
end
|
161
216
|
end
|
162
217
|
end
|
163
218
|
end
|
data/lib/one_gadget/fetcher.rb
CHANGED
@@ -15,9 +15,7 @@ module OneGadget
|
|
15
15
|
# @return [Array<OneGadget::Gadget::Gadget>?]
|
16
16
|
# +nil+ is returned if cannot find target id in database.
|
17
17
|
def from_build_id(build_id, remote: true)
|
18
|
-
|
19
|
-
raise ArgumentError, format('invalid BuildID format: %p', build_id)
|
20
|
-
end
|
18
|
+
OneGadget::Helper.verify_build_id!(build_id)
|
21
19
|
OneGadget::Gadget.builds(build_id, remote: remote)
|
22
20
|
end
|
23
21
|
|
data/lib/one_gadget/gadget.rb
CHANGED
@@ -64,6 +64,33 @@ module OneGadget
|
|
64
64
|
BUILDS[build_id]
|
65
65
|
end
|
66
66
|
|
67
|
+
# Returns the comments in builds/libc-*-<build_id>*.rb
|
68
|
+
# @param [String] build_id
|
69
|
+
# Supports give only few starting bytes, but a warning will be shown
|
70
|
+
# if multiple BulidIDs are matched.
|
71
|
+
# @return [String?]
|
72
|
+
# Lines of comments.
|
73
|
+
# @example
|
74
|
+
# puts OneGadget::Gadget.builds_info('3bbdc')
|
75
|
+
# # https://gitlab.com/libcdb/libcdb/blob/master/libc/libc6-amd64-2.19-18+deb8u4/lib64/libc-2.19.so
|
76
|
+
# #
|
77
|
+
# # Advanced Micro Devices X86-64
|
78
|
+
# # ...
|
79
|
+
def builds_info(build_id)
|
80
|
+
raise ArgumentError, "Invalid BuildID #{build_id.inspect}" if build_id =~ /[^0-9a-f]/
|
81
|
+
files = Dir.glob(File.join(BUILDS_PATH, "*-#{build_id}*.rb")).sort
|
82
|
+
return OneGadget::Logger.not_found(build_id) && nil if files.empty?
|
83
|
+
if files.size > 1
|
84
|
+
OneGadget::Logger.warn("Multiple BuildIDs match /^#{build_id}/\n")
|
85
|
+
show = files.map do |f|
|
86
|
+
File.basename(f, '.rb').reverse.split('-', 2).join(' ').reverse
|
87
|
+
end
|
88
|
+
OneGadget::Logger.warn("Candidates are:\n#{show * "\n"}\n")
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
OneGadget::Helper.comments_of_file(files.first)
|
92
|
+
end
|
93
|
+
|
67
94
|
# Add a gadget, for scripts in builds/ to use.
|
68
95
|
# @param [String] build_id The target's build id.
|
69
96
|
# @param [Integer] offset The relative address offset of this gadget.
|
data/lib/one_gadget/helper.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
require 'elftools'
|
2
1
|
require 'net/http'
|
3
2
|
require 'openssl'
|
4
3
|
require 'pathname'
|
5
4
|
require 'tempfile'
|
6
5
|
|
6
|
+
require 'elftools'
|
7
|
+
|
7
8
|
require 'one_gadget/logger'
|
8
9
|
|
9
10
|
module OneGadget
|
@@ -13,6 +14,26 @@ module OneGadget
|
|
13
14
|
BUILD_ID_FORMAT = /[0-9a-f]{40}/
|
14
15
|
# Define class methods here.
|
15
16
|
module ClassMethods
|
17
|
+
# Verify if `build_id` is a valid SHA1 hex format.
|
18
|
+
# @param [String] build_id
|
19
|
+
# BuildID.
|
20
|
+
# @raise [ArgumentError]
|
21
|
+
# Raises error if invalid.
|
22
|
+
# @return [void]
|
23
|
+
def verify_build_id!(build_id)
|
24
|
+
return if build_id =~ /\A#{OneGadget::Helper::BUILD_ID_FORMAT}\Z/
|
25
|
+
raise ArgumentError, format('invalid BuildID format: %p', build_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fetch lines start with '#'.
|
29
|
+
# @param [String] file
|
30
|
+
# Filename.
|
31
|
+
# @return [Array<String>]
|
32
|
+
# Lines of comments.
|
33
|
+
def comments_of_file(file)
|
34
|
+
File.readlines(file).map { |s| s[2..-1].rstrip if s.start_with?('# ') }.compact
|
35
|
+
end
|
36
|
+
|
16
37
|
# Get absolute path from relative path. Support symlink.
|
17
38
|
# @param [String] path Relative path.
|
18
39
|
# @return [String] Absolute path, with symlink resolved.
|
@@ -30,7 +51,7 @@ module OneGadget
|
|
30
51
|
# build_id_of('/lib/x86_64-linux-gnu/libc-2.23.so')
|
31
52
|
# #=> '60131540dadc6796cab33388349e6e4e68692053'
|
32
53
|
def build_id_of(path)
|
33
|
-
ELFTools::ELFFile.new(
|
54
|
+
File.open(path) { |f| ELFTools::ELFFile.new(f).build_id }
|
34
55
|
end
|
35
56
|
|
36
57
|
# Disable colorize.
|
@@ -48,7 +69,7 @@ module OneGadget
|
|
48
69
|
# Is colorify output enabled?
|
49
70
|
# @return [Boolean]
|
50
71
|
# True or false.
|
51
|
-
def
|
72
|
+
def color_enabled?
|
52
73
|
# if not set, use tty to check
|
53
74
|
return $stdout.tty? if @disable_color.nil?
|
54
75
|
!@disable_color
|
@@ -68,7 +89,7 @@ module OneGadget
|
|
68
89
|
# @param [Symbol] sev Specific which kind of color want to use, valid symbols are defined in +COLOR_CODE+.
|
69
90
|
# @return [String] Wrapper with color codes.
|
70
91
|
def colorize(str, sev: :normal_s)
|
71
|
-
return str unless
|
92
|
+
return str unless color_enabled?
|
72
93
|
cc = COLOR_CODE
|
73
94
|
color = cc.key?(sev) ? cc[sev] : ''
|
74
95
|
"#{color}#{str.sub(cc[:esc_m], color)}#{cc[:esc_m]}"
|
@@ -96,9 +117,8 @@ module OneGadget
|
|
96
117
|
def download_build(file)
|
97
118
|
temp = Tempfile.new(['gadgets', file + '.rb'])
|
98
119
|
url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', file + '.rb')))
|
99
|
-
temp.write
|
100
|
-
temp.close
|
101
|
-
temp
|
120
|
+
temp.write(url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', file + '.rb'))))
|
121
|
+
temp.tap(&:close)
|
102
122
|
end
|
103
123
|
|
104
124
|
# Get the latest builds list from repo.
|
@@ -132,14 +152,14 @@ module OneGadget
|
|
132
152
|
# @return [void]
|
133
153
|
def ask_update(msg: '')
|
134
154
|
name = 'one_gadget'
|
135
|
-
cmd = colorize("gem update #{name}")
|
155
|
+
cmd = colorize("gem update #{name} && gem cleanup #{name}")
|
136
156
|
OneGadget::Logger.info(msg + "\n" + "Update with: $ #{cmd}" + "\n")
|
137
157
|
end
|
138
158
|
|
139
159
|
# Fetch the file archiecture of +file+.
|
140
160
|
# @param [String] file The target ELF filename.
|
141
|
-
# @return [
|
142
|
-
# Only supports
|
161
|
+
# @return [Symbol]
|
162
|
+
# Only supports architecture amd64 and i386 now.
|
143
163
|
def architecture(file)
|
144
164
|
f = File.open(file)
|
145
165
|
str = ELFTools::ELFFile.new(f).machine
|
data/lib/one_gadget/logger.rb
CHANGED
@@ -25,13 +25,13 @@ module OneGadget
|
|
25
25
|
# @param [String] build_id
|
26
26
|
# Build ID.
|
27
27
|
def not_found(build_id)
|
28
|
-
warn("Cannot find BuildID [#{build_id}]")
|
28
|
+
warn("Cannot find BuildID [#{build_id}]\n")
|
29
29
|
[]
|
30
30
|
end
|
31
31
|
|
32
32
|
%i[info warn].each do |sym|
|
33
33
|
define_method(sym) do |msg|
|
34
|
-
@logger.
|
34
|
+
@logger.__send__(sym, msg)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/one_gadget/update.rb
CHANGED
@@ -53,8 +53,8 @@ module OneGadget
|
|
53
53
|
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
54
54
|
IO.binwrite(CACHE_FILE, '') unless File.exist?(CACHE_FILE)
|
55
55
|
CACHE_FILE
|
56
|
-
rescue # prevent dir is not writable
|
57
|
-
|
56
|
+
rescue Errno::EACCES # prevent dir is not writable
|
57
|
+
nil
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
data/lib/one_gadget/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: one_gadget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: elftools
|
@@ -24,76 +24,62 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: codeclimate-test-reporter
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.6'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.6'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
31
|
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '12.
|
33
|
+
version: '12.3'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '12.
|
40
|
+
version: '12.3'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: rspec
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - "~>"
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
47
|
+
version: '3.7'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
52
|
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
54
|
+
version: '3.7'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: rubocop
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
61
|
+
version: '0.55'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
68
|
+
version: '0.55'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: simplecov
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - "~>"
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
75
|
+
version: 0.16.1
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
82
|
+
version: 0.16.1
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: yard
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -792,12 +778,17 @@ files:
|
|
792
778
|
- lib/one_gadget/builds/libc-2.25-912fc00c0da67045111928bd5c8a350e5be18c41.rb
|
793
779
|
- lib/one_gadget/builds/libc-2.25-eae5038c2b9ae67d9eda345aa9fbe0a7185ab436.rb
|
794
780
|
- lib/one_gadget/builds/libc-2.26-2104f3d4ad5cf68603afbe7ba1a17f5ac99c5988.rb
|
781
|
+
- lib/one_gadget/builds/libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.rb
|
782
|
+
- lib/one_gadget/builds/libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.rb
|
783
|
+
- lib/one_gadget/builds/libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.rb
|
784
|
+
- lib/one_gadget/builds/libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.rb
|
795
785
|
- lib/one_gadget/emulators/amd64.rb
|
796
786
|
- lib/one_gadget/emulators/i386.rb
|
797
787
|
- lib/one_gadget/emulators/instruction.rb
|
798
788
|
- lib/one_gadget/emulators/lambda.rb
|
799
789
|
- lib/one_gadget/emulators/processor.rb
|
800
790
|
- lib/one_gadget/emulators/x86.rb
|
791
|
+
- lib/one_gadget/error.rb
|
801
792
|
- lib/one_gadget/fetcher.rb
|
802
793
|
- lib/one_gadget/fetchers/amd64.rb
|
803
794
|
- lib/one_gadget/fetchers/base.rb
|
@@ -827,7 +818,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
827
818
|
version: '0'
|
828
819
|
requirements: []
|
829
820
|
rubyforge_project:
|
830
|
-
rubygems_version: 2.
|
821
|
+
rubygems_version: 2.6.14
|
831
822
|
signing_key:
|
832
823
|
specification_version: 4
|
833
824
|
summary: one_gadget
|