one_gadget 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/CHANGELOG.md +197 -65
- data/README.md +96 -22
- data/lib/one_gadget/abi.rb +41 -5
- data/lib/one_gadget/builds/libc-2.31-93b46e0027747153e336c3fd9431ce9a5d82ad00.rb +563 -0
- data/lib/one_gadget/builds/libc-2.35-891c1403437a4e30e684e0c8e34b87a09e4298e5.rb +434 -0
- data/lib/one_gadget/builds/libc-2.39-a1d1cf4badf1f5dfe57ff1d17bd692ccc6fcd5c5.rb +531 -0
- data/lib/one_gadget/builds/libc-2.39-cd8f5a207dd67aea370d2b471a54c3e56f44ab18.rb +531 -0
- data/lib/one_gadget/builds/libc-2.43-b50ceafbd17dc6bceee344a66671c7eaa152bef4.rb +15 -0
- data/lib/one_gadget/emulators/aarch64.rb +5 -2
- data/lib/one_gadget/emulators/amd64.rb +1 -0
- data/lib/one_gadget/emulators/arm.rb +27 -18
- data/lib/one_gadget/emulators/arm_family.rb +35 -160
- data/lib/one_gadget/emulators/conditional.rb +28 -22
- data/lib/one_gadget/emulators/constraints.rb +269 -0
- data/lib/one_gadget/emulators/data_processing.rb +167 -0
- data/lib/one_gadget/emulators/i386.rb +4 -6
- data/lib/one_gadget/emulators/instruction.rb +24 -1
- data/lib/one_gadget/emulators/lambda.rb +3 -1
- data/lib/one_gadget/emulators/mips.rb +289 -0
- data/lib/one_gadget/emulators/processor.rb +33 -455
- data/lib/one_gadget/emulators/register_file.rb +19 -10
- data/lib/one_gadget/emulators/riscv64.rb +265 -0
- data/lib/one_gadget/emulators/safe_calls.rb +9 -3
- data/lib/one_gadget/emulators/tracked_memory.rb +209 -0
- data/lib/one_gadget/emulators/x86.rb +32 -22
- data/lib/one_gadget/fetchers/aarch64.rb +0 -27
- data/lib/one_gadget/fetchers/amd64.rb +0 -24
- data/lib/one_gadget/fetchers/argument_resolution.rb +340 -0
- data/lib/one_gadget/fetchers/arm.rb +99 -44
- data/lib/one_gadget/fetchers/base.rb +142 -640
- data/lib/one_gadget/fetchers/candidate_walk.rb +150 -0
- data/lib/one_gadget/fetchers/disassembly.rb +252 -0
- data/lib/one_gadget/fetchers/dynamic_symbols.rb +104 -0
- data/lib/one_gadget/fetchers/i386.rb +5 -8
- data/lib/one_gadget/fetchers/mips.rb +478 -0
- data/lib/one_gadget/fetchers/objdump.rb +24 -2
- data/lib/one_gadget/fetchers/riscv64.rb +78 -0
- data/lib/one_gadget/fetchers/x86.rb +19 -0
- data/lib/one_gadget/fetchers.rb +22 -6
- data/lib/one_gadget/gadget.rb +25 -34
- data/lib/one_gadget/helper.rb +19 -5
- data/lib/one_gadget/one_gadget.rb +6 -0
- data/lib/one_gadget/version.rb +1 -1
- data/lib/one_gadget.rb +1 -1
- metadata +18 -6
- data/lib/one_gadget/builds/libc-2.26-2104f3d4ad5cf68603afbe7ba1a17f5ac99c5988.rb +0 -227
- data/lib/one_gadget/builds/libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.rb +0 -300
- data/lib/one_gadget/builds/libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.rb +0 -199
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db6b57fdce4468596a6c52c7af146c6dafce4d98c2cc0e887f7e9cafee8ddb32
|
|
4
|
+
data.tar.gz: 6fce268e224fd67920694a1fd1ac2ae16f337f10abb3f3206bace11126a1abb7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cac3e425c402f1f4fc5c1c685f61118492d2a9bd358d2bad56962836d849b077bc70b2781f20b4fe9624e1a532ec4e07803a141667cbc43709d1d9ad84dee766
|
|
7
|
+
data.tar.gz: 0a51d45298fba885dda209ab236c2dac350d6f7abce5dc7137e5fb8994f6ffe2d0a253d835627412c3fd642b46880ce087470bcb098459b65cb9f38041b2bb4d
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,59 @@ Notable changes to one_gadget, newest first. The format follows
|
|
|
7
7
|
A change worth a user's attention lands with its entry under *Unreleased*;
|
|
8
8
|
releases are cut by giving that section a version and a date.
|
|
9
9
|
|
|
10
|
+
## v2.1.0 - 2026-09-05
|
|
11
|
+
|
|
12
|
+
Two more architectures, and a libc that ships without section headers -- as an
|
|
13
|
+
embedded one usually does -- can now be searched at all.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- RISC-V (RV64) support, alongside i386, amd64, aarch64 and arm ([#420], [#421],
|
|
18
|
+
[#422], [#423], [#425]). Every gadget it reports has been run under a debugger and
|
|
19
|
+
seen to spawn a shell ([#424]).
|
|
20
|
+
- MIPS (32-bit o32) support, in both byte orders ([#448]), verified the same way.
|
|
21
|
+
- Reads a libc that ships without section headers, as OpenWrt's musl does
|
|
22
|
+
([#432]). Every architecture reports the same gadgets as the intact file.
|
|
23
|
+
- A terminal `posix_spawn` whose symbol carries no version marker, as musl's
|
|
24
|
+
does not, is no longer passed over ([#432]).
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- The gem no longer carries gadgets for the three glibc 2.26 builds it shipped
|
|
29
|
+
([#443]) -- no supported Ubuntu LTS ships 2.26. Those BuildIDs now answer from
|
|
30
|
+
the remote database.
|
|
31
|
+
- Searching a MIPS libc takes 0.7s, was 2.3s ([#453]).
|
|
32
|
+
- Searching the same file twice in one process reuses what the first search
|
|
33
|
+
found ([#455]).
|
|
34
|
+
- Further search speedups, gadgets unchanged: objdump is asked what it supports
|
|
35
|
+
once per binary rather than once per command ([#454]), the scan locating calls
|
|
36
|
+
decodes far fewer words ([#456]), and a quarter as much is disassembled around
|
|
37
|
+
each call ([#457]).
|
|
38
|
+
- Requires elftools >= 2.2.0 ([#442], [#465]), which reads a symbol table without
|
|
39
|
+
building an object per symbol. A libc with no section headers searches in 0.24s,
|
|
40
|
+
was 0.44s; one with its sections intact is unchanged.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- 67 more gadgets across the test corpus ([#460]): a backward walk threw away any
|
|
45
|
+
path that hit its length limit, so what a libc reported depended on how far
|
|
46
|
+
back it had been disassembled. Every new gadget was run under a debugger.
|
|
47
|
+
- MIPS gadgets that could not work are no longer reported ([#460]). Entering at a
|
|
48
|
+
call leaves the callee without the address o32 requires in `t9`, and six such
|
|
49
|
+
gadgets were verified not to spawn a shell.
|
|
50
|
+
- arm found nothing at all in a libc with no section headers ([#441]): Thumb code
|
|
51
|
+
was read as A32, symbol addresses were off by one, and literal-pool loads went
|
|
52
|
+
unresolved.
|
|
53
|
+
- A libc with no section headers lost the calls at an address its symbol table
|
|
54
|
+
gives more than one name, such as `sigaction`/`__sigaction` -- 20 aarch64
|
|
55
|
+
gadgets ([#440]).
|
|
56
|
+
- A libc with no section headers reported fewer gadgets on aarch64 and RISC-V
|
|
57
|
+
than the same libc intact -- 84 across the corpus ([#439]).
|
|
58
|
+
- amd64 discarded every `execl` candidate before emulating it ([#434]). No fixture
|
|
59
|
+
reports a new gadget as a result, but one could.
|
|
60
|
+
- A search could abort with `TypeError` where it should have dropped the
|
|
61
|
+
candidate ([#434]).
|
|
62
|
+
|
|
10
63
|
## v2.0.0 - 2026-08-29
|
|
11
64
|
|
|
12
65
|
Reported gadgets are not offset-for-offset comparable with 1.x: constraints are
|
|
@@ -15,145 +68,140 @@ test corpus reports has been run under a debugger and seen to spawn a shell.
|
|
|
15
68
|
|
|
16
69
|
### Added
|
|
17
70
|
|
|
18
|
-
- ARM32 support, alongside the existing amd64, i386 and aarch64 (#316).
|
|
71
|
+
- ARM32 support, alongside the existing amd64, i386 and aarch64 ([#316]).
|
|
19
72
|
- Output level 2, which lists every gadget including the ones lower levels drop
|
|
20
|
-
as duplicate or harder-to-satisfy (#350). `--level` is documented in the
|
|
21
|
-
README with what each level shows (#397).
|
|
73
|
+
as duplicate or harder-to-satisfy ([#350]). `--level` is documented in the
|
|
74
|
+
README with what each level shows ([#397]).
|
|
22
75
|
- Aletheia, a verification harness that loads a libc under gdb (natively or
|
|
23
76
|
through qemu-user), applies a constraint-satisfying plan, and requires a real
|
|
24
|
-
shell to run `ls /` before a gadget counts as real (#399). Run it with
|
|
77
|
+
shell to run `ls /` before a gadget counts as real ([#399]). Run it with
|
|
25
78
|
`rake "aletheia:verify[level, target]"`. Every gadget the fixtures report is
|
|
26
79
|
verified this way.
|
|
27
80
|
- Gadgets say which file descriptors they close on the way to the exec, so a
|
|
28
|
-
reader knows when the spawned shell would lose its I/O (#371).
|
|
81
|
+
reader knows when the spawned shell would lose its I/O ([#371]).
|
|
29
82
|
- `rake builds:refresh` rebuilds the shipped gadget database from what the
|
|
30
83
|
Ubuntu archive publishes now, and `rake builds:check` fails when a shipped
|
|
31
|
-
entry has gone stale -- at most once a week, never on CI (#405, #406).
|
|
32
|
-
- posix_spawn gadgets on aarch64 (#317), and the string a gadget reaches
|
|
33
|
-
through the GOT is shown rather than left opaque (#372).
|
|
84
|
+
entry has gone stale -- at most once a week, never on CI ([#405], [#406]).
|
|
85
|
+
- posix_spawn gadgets on aarch64 ([#317]), and the string a gadget reaches
|
|
86
|
+
through the GOT is shown rather than left opaque ([#372]).
|
|
34
87
|
|
|
35
88
|
### Changed
|
|
36
89
|
|
|
37
|
-
- **The gem ships a curated gadget database
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
offline `-b` for an arbitrary libc no longer answers from the gem alone
|
|
42
|
-
(#405).
|
|
90
|
+
- **The gem ships a curated gadget database** -- 33 entries, taking the packaged
|
|
91
|
+
gem from 3.4MB to 112KB ([#405]). Any other BuildID is fetched on demand as
|
|
92
|
+
before, so an offline `-b` for an arbitrary libc no longer answers from the gem
|
|
93
|
+
alone.
|
|
43
94
|
- **An output level means the same thing however a libc is looked up.** `-b`
|
|
44
95
|
now honours `--level`, where it previously handed back whatever the database
|
|
45
96
|
stored, and level 1 picks the same gadget however the emulator happened to
|
|
46
|
-
find it (#402).
|
|
97
|
+
find it ([#402]).
|
|
47
98
|
- The database was regenerated; entries had been built before a year of
|
|
48
|
-
constraint work and reported gadgets their libc no longer produces (#405).
|
|
99
|
+
constraint work and reported gadgets their libc no longer produces ([#405]).
|
|
49
100
|
- Constraints read consistently: pointers compare to `NULL` and values to hex,
|
|
50
|
-
never a bare `0` (#334), and readability is stated as `readable: <reg>`
|
|
51
|
-
(#344). A mask a gadget applies is named rather than given up on (#370), and
|
|
52
|
-
a value required to be zero scores the same however it is spelled (#377,
|
|
53
|
-
#401).
|
|
101
|
+
never a bare `0` ([#334]), and readability is stated as `readable: <reg>`
|
|
102
|
+
([#344]). A mask a gadget applies is named rather than given up on ([#370]), and
|
|
103
|
+
a value required to be zero scores the same however it is spelled ([#377],
|
|
104
|
+
[#401]).
|
|
54
105
|
- Emulation stops at the terminal `exec*` call instead of running past it
|
|
55
|
-
(#325), and a candidate whose path cannot hold is rejected (#359, #360).
|
|
106
|
+
([#325]), and a candidate whose path cannot hold is rejected ([#359], [#360]).
|
|
56
107
|
- Searching a libc got roughly five times faster: the test corpus at level 0
|
|
57
|
-
went from 42.5s to 8.6s (#388, #389, #390, #393, #394, #395, #396).
|
|
58
|
-
- Requires Ruby >= 3.3 (#293) and elftools >= 2.0.0.
|
|
108
|
+
went from 42.5s to 8.6s ([#388], [#389], [#390], [#393], [#394], [#395], [#396]).
|
|
109
|
+
- Requires Ruby >= 3.3 ([#293]) and elftools >= 2.0.0.
|
|
59
110
|
|
|
60
111
|
### Fixed
|
|
61
112
|
|
|
62
113
|
- Gadgets that could not actually spawn a shell, each found by running them:
|
|
63
114
|
a dereferenced pointer argument of a "safe" libc call went unconstrained
|
|
64
|
-
(#323, #340), a writable constraint was lost when its base was reassigned
|
|
65
|
-
(#341), frame-pointer stores did not require the frame be writable (#345),
|
|
66
|
-
an argv that disables shell execution was reported as usable (#348), an
|
|
67
|
-
uncontrolled dereference was not required to be readable (#354), and a slot
|
|
115
|
+
([#323], [#340]), a writable constraint was lost when its base was reassigned
|
|
116
|
+
([#341]), frame-pointer stores did not require the frame be writable ([#345]),
|
|
117
|
+
an argv that disables shell execution was reported as usable ([#348]), an
|
|
118
|
+
uncontrolled dereference was not required to be readable ([#354]), and a slot
|
|
68
119
|
was read back as its entry value rather than what the gadget had put there
|
|
69
|
-
(#364).
|
|
120
|
+
([#364]).
|
|
70
121
|
- A GOT-base register the window establishes itself was seeded as a
|
|
71
|
-
precondition (#385), and the GOT-base constraint is now only stated where a
|
|
72
|
-
caller can actually put it -- in a register (#404).
|
|
122
|
+
precondition ([#385]), and the GOT-base constraint is now only stated where a
|
|
123
|
+
caller can actually put it -- in a register ([#404]).
|
|
73
124
|
- Windowed disassembly lost gadgets whose branch predecessor sits after the
|
|
74
|
-
call, and could invent gadgets by falling through a window boundary (#391,
|
|
75
|
-
#392).
|
|
76
|
-
- The "not glibc" check did not fire for aarch64 files (#311).
|
|
125
|
+
call, and could invent gadgets by falling through a window boundary ([#391],
|
|
126
|
+
[#392]).
|
|
127
|
+
- The "not glibc" check did not fire for aarch64 files ([#311]).
|
|
77
128
|
- **Certificates are verified again when fetching a gadget database entry.**
|
|
78
|
-
Verification had been
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
having been generated before the emulator learned to run `xchg`. They are
|
|
83
|
-
regenerated, and `rake builds:audit` now fails when an entry stops matching
|
|
129
|
+
Verification had been off, and the response is written to a file and required
|
|
130
|
+
as Ruby -- so anyone able to answer that request could run code as the user.
|
|
131
|
+
- Shipped entries reported fewer gadgets than reading the same libc as a file.
|
|
132
|
+
They are regenerated, and `rake builds:audit` now fails when one stops matching
|
|
84
133
|
its libc.
|
|
85
134
|
- An architecture is recognised by the machine type its ELF header states, not
|
|
86
|
-
by the name
|
|
87
|
-
|
|
88
|
-
reading as an unknown architecture.
|
|
135
|
+
by the name elftools prints for it, whose wording changed in 2.0.0 and left
|
|
136
|
+
every aarch64 and amd64 file reading as unknown.
|
|
89
137
|
|
|
90
138
|
## v1.10.0 - 2024-10-04
|
|
91
139
|
|
|
92
|
-
- Added JSON output, `--output-format json` (#225).
|
|
93
|
-
- Requires Ruby >= 3.1 (#216).
|
|
140
|
+
- Added JSON output, `--output-format json` ([#225]).
|
|
141
|
+
- Requires Ruby >= 3.1 ([#216]).
|
|
94
142
|
|
|
95
143
|
## v1.9.0 - 2023-11-30
|
|
96
144
|
|
|
97
|
-
- Sharper constraints for `argv` and `envp` (#206).
|
|
98
|
-
- Added `--dwarf-start=0` so the output carries no local file path (#205).
|
|
145
|
+
- Sharper constraints for `argv` and `envp` ([#206]).
|
|
146
|
+
- Added `--dwarf-start=0` so the output carries no local file path ([#205]).
|
|
99
147
|
|
|
100
148
|
## v1.8.1 - 2022-03-25
|
|
101
149
|
|
|
102
150
|
- Stack registers score higher on writable constraints, so easier gadgets rank
|
|
103
|
-
first (#190).
|
|
151
|
+
first ([#190]).
|
|
104
152
|
|
|
105
153
|
## v1.8.0 - 2022-03-20
|
|
106
154
|
|
|
107
|
-
- posix_spawn call sites are treated as one-gadgets (#183).
|
|
108
|
-
- amd64: loosened the restriction on gadgets reached by `jmp` (#178).
|
|
155
|
+
- posix_spawn call sites are treated as one-gadgets ([#183]).
|
|
156
|
+
- amd64: loosened the restriction on gadgets reached by `jmp` ([#178]).
|
|
109
157
|
|
|
110
158
|
## v1.7.4 - 2021-01-13
|
|
111
159
|
|
|
112
|
-
- Writable constraints on the x86 architectures (#156).
|
|
160
|
+
- Writable constraints on the x86 architectures ([#156]).
|
|
113
161
|
|
|
114
162
|
## v1.7.3 - 2019-10-25
|
|
115
163
|
|
|
116
|
-
- Added the `--base` option (#100).
|
|
117
|
-
- Constraints for XMM alignment, and stack alignment scores higher (#87).
|
|
164
|
+
- Added the `--base` option ([#100]).
|
|
165
|
+
- Constraints for XMM alignment, and stack alignment scores higher ([#87]).
|
|
118
166
|
|
|
119
167
|
## v1.7.2 - 2019-05-05
|
|
120
168
|
|
|
121
|
-
- Added `--near`, ordering gadgets by distance from given functions (#76).
|
|
122
|
-
- Introduced the CLI module (#81).
|
|
169
|
+
- Added `--near`, ordering gadgets by distance from given functions ([#76]).
|
|
170
|
+
- Introduced the CLI module ([#81]).
|
|
123
171
|
|
|
124
172
|
## v1.7.1 - 2019-04-19
|
|
125
173
|
|
|
126
|
-
- Added gemspec metadata (#75).
|
|
174
|
+
- Added gemspec metadata ([#75]).
|
|
127
175
|
|
|
128
176
|
## v1.7.0 - 2019-03-07
|
|
129
177
|
|
|
130
|
-
- AArch64 support (#64).
|
|
131
|
-
- Constraints are scored, so the easiest gadgets are reported first (#67).
|
|
178
|
+
- AArch64 support ([#64]).
|
|
179
|
+
- Constraints are scored, so the easiest gadgets are reported first ([#67]).
|
|
132
180
|
|
|
133
181
|
## v1.6.2 - 2018-10-25
|
|
134
182
|
|
|
135
|
-
- one_gadget is usable as a library (#47).
|
|
183
|
+
- one_gadget is usable as a library ([#47]).
|
|
136
184
|
|
|
137
185
|
## v1.6.1 - 2018-09-13
|
|
138
186
|
|
|
139
|
-
- Improved update checking (#43) and custom error classes (#40).
|
|
187
|
+
- Improved update checking ([#43]) and custom error classes ([#40]).
|
|
140
188
|
|
|
141
189
|
## v1.6.0 - 2018-04-28
|
|
142
190
|
|
|
143
|
-
- Added the xmm instructions and glibc 2.27 builds (#27).
|
|
144
|
-
- Added `--info`, showing what is known about a BuildID (#25).
|
|
191
|
+
- Added the xmm instructions and glibc 2.27 builds ([#27]).
|
|
192
|
+
- Added `--info`, showing what is known about a BuildID ([#25]).
|
|
145
193
|
|
|
146
194
|
## v1.5.0 - 2017-11-06
|
|
147
195
|
|
|
148
|
-
- Emulator rework, and the build database is generated from libc files (#15).
|
|
196
|
+
- Emulator rework, and the build database is generated from libc files ([#15]).
|
|
149
197
|
|
|
150
198
|
## v1.4.1 - 2017-11-01
|
|
151
199
|
|
|
152
|
-
- Checks for a newer version automatically (#13).
|
|
200
|
+
- Checks for a newer version automatically ([#13]).
|
|
153
201
|
|
|
154
202
|
## v1.4.0 - 2017-06-23
|
|
155
203
|
|
|
156
|
-
- Warns when a BuildID is not found, and added Ubuntu 16.04/17.04 libcs (#9).
|
|
204
|
+
- Warns when a BuildID is not found, and added Ubuntu 16.04/17.04 libcs ([#9]).
|
|
157
205
|
|
|
158
206
|
## v1.3.8 - 2017-05-26
|
|
159
207
|
|
|
@@ -166,7 +214,7 @@ test corpus reports has been run under a debugger and seen to spawn a shell.
|
|
|
166
214
|
## v1.3.6 - 2017-04-01
|
|
167
215
|
|
|
168
216
|
- Added builds for glibc >= 2.19; a given libc file is no longer looked up
|
|
169
|
-
remotely (#6).
|
|
217
|
+
remotely ([#6]).
|
|
170
218
|
|
|
171
219
|
## v1.3.5 - 2017-03-27
|
|
172
220
|
|
|
@@ -215,3 +263,87 @@ test corpus reports has been run under a debugger and seen to spawn a shell.
|
|
|
215
263
|
## v0.1.0 - 2017-02-11
|
|
216
264
|
|
|
217
265
|
- First release.
|
|
266
|
+
|
|
267
|
+
[#6]: https://github.com/david942j/one_gadget/pull/6
|
|
268
|
+
[#9]: https://github.com/david942j/one_gadget/pull/9
|
|
269
|
+
[#13]: https://github.com/david942j/one_gadget/pull/13
|
|
270
|
+
[#15]: https://github.com/david942j/one_gadget/pull/15
|
|
271
|
+
[#25]: https://github.com/david942j/one_gadget/pull/25
|
|
272
|
+
[#27]: https://github.com/david942j/one_gadget/pull/27
|
|
273
|
+
[#40]: https://github.com/david942j/one_gadget/pull/40
|
|
274
|
+
[#43]: https://github.com/david942j/one_gadget/pull/43
|
|
275
|
+
[#47]: https://github.com/david942j/one_gadget/pull/47
|
|
276
|
+
[#64]: https://github.com/david942j/one_gadget/pull/64
|
|
277
|
+
[#67]: https://github.com/david942j/one_gadget/pull/67
|
|
278
|
+
[#75]: https://github.com/david942j/one_gadget/pull/75
|
|
279
|
+
[#76]: https://github.com/david942j/one_gadget/pull/76
|
|
280
|
+
[#81]: https://github.com/david942j/one_gadget/pull/81
|
|
281
|
+
[#87]: https://github.com/david942j/one_gadget/pull/87
|
|
282
|
+
[#100]: https://github.com/david942j/one_gadget/pull/100
|
|
283
|
+
[#156]: https://github.com/david942j/one_gadget/pull/156
|
|
284
|
+
[#178]: https://github.com/david942j/one_gadget/pull/178
|
|
285
|
+
[#183]: https://github.com/david942j/one_gadget/pull/183
|
|
286
|
+
[#190]: https://github.com/david942j/one_gadget/pull/190
|
|
287
|
+
[#205]: https://github.com/david942j/one_gadget/pull/205
|
|
288
|
+
[#206]: https://github.com/david942j/one_gadget/pull/206
|
|
289
|
+
[#216]: https://github.com/david942j/one_gadget/pull/216
|
|
290
|
+
[#225]: https://github.com/david942j/one_gadget/pull/225
|
|
291
|
+
[#293]: https://github.com/david942j/one_gadget/pull/293
|
|
292
|
+
[#311]: https://github.com/david942j/one_gadget/pull/311
|
|
293
|
+
[#316]: https://github.com/david942j/one_gadget/pull/316
|
|
294
|
+
[#317]: https://github.com/david942j/one_gadget/pull/317
|
|
295
|
+
[#323]: https://github.com/david942j/one_gadget/pull/323
|
|
296
|
+
[#325]: https://github.com/david942j/one_gadget/pull/325
|
|
297
|
+
[#334]: https://github.com/david942j/one_gadget/pull/334
|
|
298
|
+
[#340]: https://github.com/david942j/one_gadget/pull/340
|
|
299
|
+
[#341]: https://github.com/david942j/one_gadget/pull/341
|
|
300
|
+
[#344]: https://github.com/david942j/one_gadget/pull/344
|
|
301
|
+
[#345]: https://github.com/david942j/one_gadget/pull/345
|
|
302
|
+
[#348]: https://github.com/david942j/one_gadget/pull/348
|
|
303
|
+
[#350]: https://github.com/david942j/one_gadget/pull/350
|
|
304
|
+
[#354]: https://github.com/david942j/one_gadget/pull/354
|
|
305
|
+
[#359]: https://github.com/david942j/one_gadget/pull/359
|
|
306
|
+
[#360]: https://github.com/david942j/one_gadget/pull/360
|
|
307
|
+
[#364]: https://github.com/david942j/one_gadget/pull/364
|
|
308
|
+
[#370]: https://github.com/david942j/one_gadget/pull/370
|
|
309
|
+
[#371]: https://github.com/david942j/one_gadget/pull/371
|
|
310
|
+
[#372]: https://github.com/david942j/one_gadget/pull/372
|
|
311
|
+
[#377]: https://github.com/david942j/one_gadget/pull/377
|
|
312
|
+
[#385]: https://github.com/david942j/one_gadget/pull/385
|
|
313
|
+
[#388]: https://github.com/david942j/one_gadget/pull/388
|
|
314
|
+
[#389]: https://github.com/david942j/one_gadget/pull/389
|
|
315
|
+
[#390]: https://github.com/david942j/one_gadget/pull/390
|
|
316
|
+
[#391]: https://github.com/david942j/one_gadget/pull/391
|
|
317
|
+
[#392]: https://github.com/david942j/one_gadget/pull/392
|
|
318
|
+
[#393]: https://github.com/david942j/one_gadget/pull/393
|
|
319
|
+
[#394]: https://github.com/david942j/one_gadget/pull/394
|
|
320
|
+
[#395]: https://github.com/david942j/one_gadget/pull/395
|
|
321
|
+
[#396]: https://github.com/david942j/one_gadget/pull/396
|
|
322
|
+
[#397]: https://github.com/david942j/one_gadget/pull/397
|
|
323
|
+
[#399]: https://github.com/david942j/one_gadget/pull/399
|
|
324
|
+
[#401]: https://github.com/david942j/one_gadget/pull/401
|
|
325
|
+
[#402]: https://github.com/david942j/one_gadget/pull/402
|
|
326
|
+
[#404]: https://github.com/david942j/one_gadget/pull/404
|
|
327
|
+
[#405]: https://github.com/david942j/one_gadget/pull/405
|
|
328
|
+
[#406]: https://github.com/david942j/one_gadget/pull/406
|
|
329
|
+
[#420]: https://github.com/david942j/one_gadget/pull/420
|
|
330
|
+
[#421]: https://github.com/david942j/one_gadget/pull/421
|
|
331
|
+
[#422]: https://github.com/david942j/one_gadget/pull/422
|
|
332
|
+
[#423]: https://github.com/david942j/one_gadget/pull/423
|
|
333
|
+
[#424]: https://github.com/david942j/one_gadget/pull/424
|
|
334
|
+
[#425]: https://github.com/david942j/one_gadget/pull/425
|
|
335
|
+
[#432]: https://github.com/david942j/one_gadget/pull/432
|
|
336
|
+
[#434]: https://github.com/david942j/one_gadget/pull/434
|
|
337
|
+
[#439]: https://github.com/david942j/one_gadget/pull/439
|
|
338
|
+
[#440]: https://github.com/david942j/one_gadget/pull/440
|
|
339
|
+
[#441]: https://github.com/david942j/one_gadget/pull/441
|
|
340
|
+
[#442]: https://github.com/david942j/one_gadget/pull/442
|
|
341
|
+
[#443]: https://github.com/david942j/one_gadget/pull/443
|
|
342
|
+
[#448]: https://github.com/david942j/one_gadget/pull/448
|
|
343
|
+
[#453]: https://github.com/david942j/one_gadget/pull/453
|
|
344
|
+
[#454]: https://github.com/david942j/one_gadget/pull/454
|
|
345
|
+
[#455]: https://github.com/david942j/one_gadget/pull/455
|
|
346
|
+
[#456]: https://github.com/david942j/one_gadget/pull/456
|
|
347
|
+
[#457]: https://github.com/david942j/one_gadget/pull/457
|
|
348
|
+
[#460]: https://github.com/david942j/one_gadget/pull/460
|
|
349
|
+
[#465]: https://github.com/david942j/one_gadget/pull/465
|
data/README.md
CHANGED
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
## OneGadget
|
|
13
13
|
|
|
14
|
-
When
|
|
15
|
-
|
|
14
|
+
When solving CTF pwn challenges we usually want a one-gadget RCE: a single address in
|
|
15
|
+
libc that, jumped to, calls `execve("/bin/sh", NULL, NULL)`.
|
|
16
16
|
|
|
17
|
-
This gem
|
|
17
|
+
This gem finds them for you, so you don't have to dig through objdump or IDA Pro every
|
|
18
|
+
time :wink:
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
Point it at a libc -- `one_gadget /path/to/libc` -- and enjoy the magic :laughing:
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
22
23
|
|
|
@@ -25,18 +26,19 @@ Available on RubyGems.org!
|
|
|
25
26
|
$ gem install one_gadget
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
Note: requires ruby version >= 2.1.0, you can use `ruby --version` to check.
|
|
29
|
-
|
|
30
29
|
## Supported Architectures
|
|
31
30
|
|
|
32
31
|
- [x] i386
|
|
33
32
|
- [x] amd64 (x86-64)
|
|
34
33
|
- [x] aarch64 (ARMv8)
|
|
35
34
|
- [x] arm (ARMv7, A32/Thumb-2)
|
|
35
|
+
- [x] riscv64 (RV64GC)
|
|
36
|
+
- [x] mips (MIPS32 o32, big- and little-endian)
|
|
36
37
|
|
|
37
38
|
## Implementation
|
|
38
39
|
|
|
39
|
-
OneGadget
|
|
40
|
+
OneGadget symbolically executes the code around each candidate address to work out what
|
|
41
|
+
has to hold for the gadget to work.
|
|
40
42
|
|
|
41
43
|
Gadgets are found by walking the control-flow graph backward from each `exec`/`posix_spawn`
|
|
42
44
|
call, following conditional branches both ways. When a gadget is only reachable if a branch is
|
|
@@ -144,17 +146,17 @@ BuildID is fetched from this repository, which keeps them all.
|
|
|
144
146
|
|
|
145
147
|
##### Why
|
|
146
148
|
|
|
147
|
-
Consider this
|
|
148
|
-
1.
|
|
149
|
-
2.
|
|
149
|
+
Consider this situation while exploiting:
|
|
150
|
+
1. You can write to the GOT (Global Offset Table).
|
|
151
|
+
2. The base address of libc is *unknown*.
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
If the function
|
|
153
|
-
|
|
153
|
+
Here you can overwrite the low two bytes of a GOT entry with the low two bytes of a
|
|
154
|
+
one-gadget. If the function sits close enough to the gadget, only one nibble is left to
|
|
155
|
+
guess -- at least a 1 in 16 chance of success.
|
|
154
156
|
|
|
155
157
|
##### Usage
|
|
156
158
|
|
|
157
|
-
|
|
159
|
+
Sorts the gadgets by how far they are from the functions you name.
|
|
158
160
|
|
|
159
161
|
```bash
|
|
160
162
|
$ one_gadget spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so --near exit,mkdir
|
|
@@ -204,7 +206,7 @@ $ one_gadget spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so --n
|
|
|
204
206
|
```
|
|
205
207
|

|
|
206
208
|
|
|
207
|
-
Regular
|
|
209
|
+
Regular expressions work too.
|
|
208
210
|
```bash
|
|
209
211
|
$ one_gadget spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so --near 'write.*' --raw
|
|
210
212
|
# [OneGadget] Gadgets near writev(0x1144a0):
|
|
@@ -216,7 +218,7 @@ $ one_gadget spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so --n
|
|
|
216
218
|
|
|
217
219
|
```
|
|
218
220
|
|
|
219
|
-
Pass an ELF file
|
|
221
|
+
Pass an ELF file instead, and OneGadget uses every function in its GOT.
|
|
220
222
|
```bash
|
|
221
223
|
$ one_gadget spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so --near spec/data/test_near_file.elf --raw
|
|
222
224
|
# [OneGadget] Gadgets near exit(0x46a70):
|
|
@@ -573,9 +575,81 @@ $ one_gadget spec/data/arm-libc-2.39.so
|
|
|
573
575
|
|
|
574
576
|
```
|
|
575
577
|
|
|
578
|
+
##### RISC-V (RV64)
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
$ one_gadget spec/data/riscv64-libc-2.39.so
|
|
582
|
+
# 0x9f73a execve("/bin/sh", s1, a2)
|
|
583
|
+
# constraints:
|
|
584
|
+
# [s1] == NULL || s1 == NULL || s1 is a valid argv
|
|
585
|
+
# [a2] == NULL || a2 == NULL || a2 is a valid envp
|
|
586
|
+
#
|
|
587
|
+
# 0x9f73c execve("/bin/sh", a1, a2)
|
|
588
|
+
# constraints:
|
|
589
|
+
# [a1] == NULL || a1 == NULL || a1 is a valid argv
|
|
590
|
+
# [a2] == NULL || a2 == NULL || a2 is a valid envp
|
|
591
|
+
#
|
|
592
|
+
# 0x9f778 execve("/bin/sh", s1, s2)
|
|
593
|
+
# constraints:
|
|
594
|
+
# [s1] == NULL || s1 == NULL || s1 is a valid argv
|
|
595
|
+
# [s2] == NULL || s2 == NULL || s2 is a valid envp
|
|
596
|
+
#
|
|
597
|
+
# 0xb5adc posix_spawn(sp+0x44, "/bin/sh", [sp+0x30], 0, sp+0x50, environ)
|
|
598
|
+
# constraints:
|
|
599
|
+
# [sp+0x50] == NULL || {[sp+0x50], [sp+0x58], [sp+0x60], [sp+0x68], ...} is a valid argv
|
|
600
|
+
# [sp+0x30] == NULL || (s32)[[sp+0x30]+0x4] <= 0x0
|
|
601
|
+
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
##### MIPS
|
|
605
|
+
|
|
606
|
+
Big-endian is as common as little-endian here, and both are read. This one is
|
|
607
|
+
OpenWrt's musl for ath79, which ships with no section headers -- the shape a
|
|
608
|
+
router's libc usually arrives in.
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
$ one_gadget spec/data/mips-musl-1.2.4.so
|
|
612
|
+
# 0x668e8 posix_spawn(sp+0x20, "/bin/sh", 0, s0, sp+0x28, environ)
|
|
613
|
+
# constraints:
|
|
614
|
+
# gp is the GOT address of libc
|
|
615
|
+
# {"sh", "-c", s1, NULL} is a valid argv
|
|
616
|
+
# s0 == NULL || (u16)[s0] == 0x0
|
|
617
|
+
#
|
|
618
|
+
# 0x668ec posix_spawn(sp+0x20, "/bin/sh", 0, s0, sp+0x28, environ)
|
|
619
|
+
# constraints:
|
|
620
|
+
# gp is the GOT address of libc
|
|
621
|
+
# {"sh", "-c", [sp+0x30], NULL} is a valid argv
|
|
622
|
+
# s0 == NULL || (u16)[s0] == 0x0
|
|
623
|
+
#
|
|
624
|
+
# 0x668f0 posix_spawn(sp+0x20, "/bin/sh", 0, a3, sp+0x28, environ)
|
|
625
|
+
# constraints:
|
|
626
|
+
# gp is the GOT address of libc
|
|
627
|
+
# {"sh", "-c", [sp+0x30], NULL} is a valid argv
|
|
628
|
+
# a3 == NULL || (u16)[a3] == 0x0
|
|
629
|
+
#
|
|
630
|
+
# 0x668f4 posix_spawn(sp+0x20, "/bin/sh", 0, a3, sp+0x28, environ)
|
|
631
|
+
# constraints:
|
|
632
|
+
# gp is the GOT address of libc
|
|
633
|
+
# v0-0x7c70 == NULL || {v0-0x7c70, "-c", [sp+0x30], NULL} is a valid argv
|
|
634
|
+
# a3 == NULL || (u16)[a3] == 0x0
|
|
635
|
+
#
|
|
636
|
+
# 0x77b44 posix_spawn(sp+0x20, "/bin/sh", s2, 0, sp+0x2c, environ)
|
|
637
|
+
# constraints:
|
|
638
|
+
# gp is the GOT address of libc
|
|
639
|
+
# {"sh", "-c", s7, NULL} is a valid argv
|
|
640
|
+
# s2 == NULL || (s32)[s2+0x4] <= 0x0
|
|
641
|
+
#
|
|
642
|
+
# 0x77b48 posix_spawn(sp+0x20, "/bin/sh", s2, 0, sp+0x2c, environ)
|
|
643
|
+
# constraints:
|
|
644
|
+
# gp is the GOT address of libc
|
|
645
|
+
# v0-0x7c70 == NULL || {v0-0x7c70, "-c", s7, NULL} is a valid argv
|
|
646
|
+
# s2 == NULL || (s32)[s2+0x4] <= 0x0
|
|
647
|
+
|
|
648
|
+
```
|
|
649
|
+
|
|
576
650
|
#### Combine with Script
|
|
577
|
-
Pass your exploit script
|
|
578
|
-
|
|
651
|
+
Pass your exploit script to `one_gadget` and it runs the script once per gadget, so you
|
|
652
|
+
don't have to work through them by hand.
|
|
579
653
|
|
|
580
654
|
```bash
|
|
581
655
|
$ one_gadget spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so -s 'echo "offset ->"'
|
|
@@ -589,7 +663,7 @@ require 'one_gadget'
|
|
|
589
663
|
OneGadget.gadgets(file: 'spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so')
|
|
590
664
|
#=> [932657, 932660, 932896, 1080554]
|
|
591
665
|
|
|
592
|
-
# or
|
|
666
|
+
# or, more briefly
|
|
593
667
|
one_gadget('spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so', level: 1)
|
|
594
668
|
#=> [335352, 335403, 335410, 335417, 335424, 335429, 335445, 335450, 541029, 541036, 541043, 541046, 541051, 541056, 541068, 541074, 541081, 541088, 932657, 932660, 932896, 933158, 933256, 933264, 933266, 933273, 933280, 933335, 933345, 1078759, 1078769, 1080554]
|
|
595
669
|
|
|
@@ -611,7 +685,7 @@ one_gadget('spec/data/libc-2.31-9fdb74e7b217d06c93172a8243f8547f947ee6d1.so')
|
|
|
611
685
|
```
|
|
612
686
|
|
|
613
687
|
## Make OneGadget Better
|
|
614
|
-
Any suggestion or feature request is welcome
|
|
688
|
+
Any suggestion or feature request is welcome, and pull requests even more so!
|
|
615
689
|
|
|
616
|
-
Please let me know if you
|
|
617
|
-
|
|
690
|
+
Please let me know if you come across a libc OneGadget finds nothing in. And if you like
|
|
691
|
+
this work, I'd be happy to be [starred](https://github.com/david942j/one_gadget/stargazers) :grimacing:
|
data/lib/one_gadget/abi.rb
CHANGED
|
@@ -14,6 +14,17 @@ module OneGadget
|
|
|
14
14
|
# Registers of AArch64.
|
|
15
15
|
AARCH64 = %w[xzr wzr sp] + 0.upto(30).map { |i| ["x#{i}", "w#{i}"] }.flatten
|
|
16
16
|
|
|
17
|
+
# Registers of RISC-V (RV64), by the ABI names objdump prints: it emits neither
|
|
18
|
+
# the numbered +x0+-+x15+ names nor +fp+ for +s0+, so neither is listed.
|
|
19
|
+
RISCV64 = %w[zero ra sp gp tp] + 0.upto(6).map { |i| "t#{i}" } +
|
|
20
|
+
0.upto(11).map { |i| "s#{i}" } + 0.upto(7).map { |i| "a#{i}" }
|
|
21
|
+
|
|
22
|
+
# Registers of MIPS (32-bit), by the names objdump prints: it emits the ABI
|
|
23
|
+
# names rather than the numbered +$0+-+$31+, and +fp+ rather than +s8+.
|
|
24
|
+
MIPS = %w[zero at v0 v1 gp sp fp ra] +
|
|
25
|
+
0.upto(3).map { |i| "a#{i}" } + 0.upto(9).map { |i| "t#{i}" } +
|
|
26
|
+
0.upto(7).map { |i| "s#{i}" } + 0.upto(1).map { |i| "k#{i}" }
|
|
27
|
+
|
|
17
28
|
# Registers of ARM (32-bit).
|
|
18
29
|
# objdump never prints the numbered name of a register that has a role name:
|
|
19
30
|
# +r10+-+r15+ always appear as +sl+/+fp+/+ip+/+sp+/+lr+/+pc+, so those are
|
|
@@ -31,7 +42,12 @@ module OneGadget
|
|
|
31
42
|
8.upto(15).map { |i| ["r#{i}d", "r#{i}"] }).to_h,
|
|
32
43
|
i386: {},
|
|
33
44
|
aarch64: 0.upto(30).to_h { |i| ["w#{i}", "x#{i}"] }.merge('wzr' => 'xzr'),
|
|
34
|
-
arm: {}
|
|
45
|
+
arm: {},
|
|
46
|
+
# RISC-V names no part of a register: its 32-bit operations are instructions
|
|
47
|
+
# of their own (+addiw+, +sext.w+), each writing the whole register.
|
|
48
|
+
riscv64: {},
|
|
49
|
+
# MIPS names no part of a register either.
|
|
50
|
+
mips: {}
|
|
35
51
|
}.freeze
|
|
36
52
|
|
|
37
53
|
# Registers a call may destroy, per each ABI's calling convention: the return
|
|
@@ -49,11 +65,19 @@ module OneGadget
|
|
|
49
65
|
aarch64: (0.upto(7).to_a + 9.upto(18).to_a).map { |i| "x#{i}" },
|
|
50
66
|
# AAPCS: r0-r3 arguments and return, ip (r12) the intra-procedure scratch,
|
|
51
67
|
# and lr, which the call itself overwrites with the return address.
|
|
52
|
-
arm: %w[r0 r1 r2 r3 ip lr]
|
|
68
|
+
arm: %w[r0 r1 r2 r3 ip lr],
|
|
69
|
+
# RISC-V LP64: a0-a7 arguments (a0/a1 also the return), t0-t6 temporaries,
|
|
70
|
+
# and ra, which the call itself overwrites with the return address.
|
|
71
|
+
riscv64: %w[ra] + 0.upto(7).map { |i| "a#{i}" } + 0.upto(6).map { |i| "t#{i}" },
|
|
72
|
+
# o32: v0/v1 the return, a0-a3 arguments, t0-t9 temporaries, at the
|
|
73
|
+
# assembler's scratch, and ra, which the call itself overwrites. gp goes
|
|
74
|
+
# with them: PIC code reloads it from the stack after every call, because
|
|
75
|
+
# the callee establishes its own.
|
|
76
|
+
mips: %w[at v0 v1 gp ra] + 0.upto(3).map { |i| "a#{i}" } + 0.upto(9).map { |i| "t#{i}" }
|
|
53
77
|
}.freeze
|
|
54
78
|
|
|
55
79
|
# The register each ABI leaves an integer return value in.
|
|
56
|
-
RETURN_REGISTER = { amd64: 'rax', i386: 'eax', aarch64: 'x0', arm: 'r0' }.freeze
|
|
80
|
+
RETURN_REGISTER = { amd64: 'rax', i386: 'eax', aarch64: 'x0', arm: 'r0', riscv64: 'a0' }.freeze
|
|
57
81
|
|
|
58
82
|
module_function
|
|
59
83
|
|
|
@@ -81,10 +105,22 @@ module OneGadget
|
|
|
81
105
|
ARM
|
|
82
106
|
end
|
|
83
107
|
|
|
108
|
+
# Registers' name of RISC-V (RV64).
|
|
109
|
+
# @return [Array<String>] List of registers.
|
|
110
|
+
def riscv64
|
|
111
|
+
RISCV64
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Registers' name of MIPS (32-bit).
|
|
115
|
+
# @return [Array<String>] List of registers.
|
|
116
|
+
def mips
|
|
117
|
+
MIPS
|
|
118
|
+
end
|
|
119
|
+
|
|
84
120
|
# Returns all names of registers.
|
|
85
121
|
# @return [Array<String>] List of registers.
|
|
86
122
|
def all
|
|
87
|
-
amd64 + aarch64 + arm
|
|
123
|
+
amd64 + aarch64 + arm + riscv64 + mips
|
|
88
124
|
end
|
|
89
125
|
|
|
90
126
|
# Checks if the register is a stack-related pointer.
|
|
@@ -92,7 +128,7 @@ module OneGadget
|
|
|
92
128
|
# Register's name.
|
|
93
129
|
# @return [Boolean] +true+ if +reg+ is a stack or frame pointer (e.g. +rsp+, +rbp+, +sp+).
|
|
94
130
|
def stack_register?(reg)
|
|
95
|
-
%w[esp ebp rsp rbp sp x29].include?(reg)
|
|
131
|
+
%w[esp ebp rsp rbp sp x29 s0].include?(reg)
|
|
96
132
|
end
|
|
97
133
|
end
|
|
98
134
|
end
|