Skip to content

Commit b42e029

Browse files
committed
Land rapid7#5067, @wchen-r7's standalone jsobfu tool
2 parents 56dc7af + cb08e5b commit b42e029

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

spec/tools/jsobfu_spec.rb

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
require 'spec_helper'
2+
3+
load Metasploit::Framework.root.join('tools/jsobfu.rb').to_path
4+
5+
require 'stringio'
6+
7+
describe Jsobfu do
8+
9+
let(:fname) {
10+
'test.js'
11+
}
12+
13+
let(:js) {
14+
%Q|alert("test");|
15+
}
16+
17+
describe Jsobfu::Driver do
18+
19+
subject do
20+
Jsobfu::Driver.new
21+
end
22+
23+
describe '#run' do
24+
let(:default_opts) {
25+
{ :input => fname, :iteration => 1 }
26+
}
27+
28+
before(:each) do
29+
allow(Jsobfu::OptsConsole).to receive(:parse).with(any_args).and_return(default_opts)
30+
allow(File).to receive(:open).with(fname, 'rb').and_yield(StringIO.new(js))
31+
@out = $stdout
32+
$stdout = StringIO.new
33+
$stdout.string = ''
34+
end
35+
36+
after(:each) do
37+
$stdout = @out
38+
end
39+
40+
context 'when a javascript file is given' do
41+
it 'returns an String' do
42+
subject.run
43+
expect($stdout.string).to be_a(String)
44+
end
45+
46+
it 'returns a non empty String' do
47+
subject.run
48+
expect($stdout.string).not_to be_empty
49+
end
50+
51+
it 'returns an String different than the original' do
52+
subject.run
53+
expect($stdout.string).not_to eq(js)
54+
end
55+
end
56+
end
57+
end
58+
59+
60+
describe Jsobfu::OptsConsole do
61+
subject do
62+
Jsobfu::OptsConsole
63+
end
64+
65+
context 'when no options are given' do
66+
it 'raises OptionParser::MissingArgument' do
67+
expect{subject.parse([])}.to raise_error(OptionParser::MissingArgument)
68+
end
69+
end
70+
71+
context 'when -t isn\'t a number' do
72+
it 'raises OptionParser::MissingArgument' do
73+
args = "-i #{fname} -t NaN".split
74+
expect{subject.parse(args)}.to raise_error(OptionParser::InvalidOption)
75+
end
76+
end
77+
end
78+
79+
end

tools/jsobfu.rb

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env ruby
2+
3+
msfbase = __FILE__
4+
while File.symlink?(msfbase)
5+
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
6+
end
7+
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib')))
8+
require 'msfenv'
9+
require 'rex'
10+
require 'msf/core'
11+
require 'msf/base'
12+
require 'optparse'
13+
14+
module Jsobfu
15+
class OptsConsole
16+
def self.parse(args)
17+
options = {}
18+
parser = OptionParser.new do |opt|
19+
opt.banner = "Usage: #{__FILE__} [options]"
20+
opt.separator ''
21+
opt.separator 'Specific options:'
22+
23+
opt.on('-t', '--iteration <Fixnum>', "Number of times to obfuscate the JavaScript") do |v|
24+
options[:iteration] = v
25+
end
26+
27+
opt.on('-i', '--input <String>', "The JavaScript file you want to obfuscate (default=1)") do |v|
28+
options[:input] = v
29+
end
30+
31+
opt.on('-o', '--output <String>', "Save the obfuscated file as") do |v|
32+
options[:output] = v
33+
end
34+
35+
opt.on_tail('-h', '--help', 'Show this message') do
36+
$stdout.puts opt
37+
exit
38+
end
39+
end
40+
41+
parser.parse!(args)
42+
43+
if options.empty?
44+
raise OptionParser::MissingArgument, 'No options set, try -h for usage'
45+
elsif options[:iteration] && options[:iteration] !~ /^\d+$/
46+
raise OptionParser::InvalidOption, "#{options[:format]} is not a number"
47+
elsif !::File.exists?(options[:input].to_s)
48+
raise OptionParser::InvalidOption, "Cannot find: #{options[:input]}"
49+
end
50+
51+
options[:iteration] = 1 unless options[:iteration]
52+
53+
options
54+
end
55+
end
56+
57+
class Driver
58+
def initialize
59+
begin
60+
@opts = OptsConsole.parse(ARGV)
61+
rescue OptionParser::ParseError => e
62+
$stderr.puts "[x] #{e.message}"
63+
exit
64+
end
65+
end
66+
67+
def run
68+
original_js = read_js(@opts[:input])
69+
js = ::Rex::Exploitation::JSObfu.new(original_js)
70+
js.obfuscate(:iterations=>@opts[:iteration].to_i)
71+
js = js.to_s
72+
73+
output_stream = $stdout
74+
output_stream.binmode
75+
output_stream.write js
76+
$stderr.puts
77+
78+
if @opts[:output]
79+
save_as(js, @opts[:output])
80+
end
81+
end
82+
83+
private
84+
85+
def read_js(path)
86+
js = ::File.open(path, 'rb') { |f| js = f.read }
87+
js
88+
end
89+
90+
def save_as(js, outfile)
91+
File.open(outfile, 'wb') do |f|
92+
f.write(js)
93+
end
94+
95+
$stderr.puts
96+
$stderr.puts "File saved as: #{outfile}"
97+
end
98+
99+
end
100+
end
101+
102+
103+
if __FILE__ == $PROGRAM_NAME
104+
driver = Jsobfu::Driver.new
105+
driver.run
106+
end

0 commit comments

Comments
 (0)