Skip to content

Commit 162b6a8

Browse files
committed
Add output spec
1 parent 589d235 commit 162b6a8

File tree

2 files changed

+131
-10
lines changed

2 files changed

+131
-10
lines changed

lib/rex/exploitation/powershell/output.rb

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ def to_s_lineno
3535
code.split(/\r\n|\n/).each_with_index do |line,idx|
3636
numbered << "#{idx}: #{line}"
3737
end
38-
return numbered
38+
39+
numbered
3940
end
4041

4142
#
@@ -119,14 +120,10 @@ def gzip_code(eof = nil)
119120
#
120121
# @param eof [String] End of file identifier to append to code
121122
# @param gzip [Boolean] Whether to use gzip compression or deflate
122-
# @param in_place [Boolean] Whether to update the current script
123-
# code or just return the output.
124123
#
125124
# @return [String] Compressed code wrapped in decompression stub
126-
def compress_code(eof = nil, gzip = true, in_place = true)
127-
code = gzip ? gzip_code(eof) : deflate_code(eof)
128-
@code = code if in_place
129-
return code
125+
def compress_code(eof = nil, gzip = true)
126+
@code = gzip ? gzip_code(eof) : deflate_code(eof)
130127
end
131128

132129
#
@@ -138,9 +135,18 @@ def decompress_code
138135
# Extract substring with payload
139136
encoded_stream = @code.scan(/FromBase64String\('(.*)'/).flatten.first
140137
# Decode and decompress the string
141-
@code = ( Rex::Text.ungzip( Rex::Text.decode_base64(encoded_stream) ) ||
142-
Rex::Text.zlib_inflate( Rex::Text.decode_base64(encoded_stream)) )
143-
return code
138+
unencoded = Rex::Text.decode_base64(encoded_stream)
139+
begin
140+
@code = Rex::Text.ungzip(unencoded) || Rex::Text.zlib_inflate(unencoded)
141+
rescue Zlib::GzipFile::Error
142+
begin
143+
@code = Rex::Text.zlib_inflate(unencoded)
144+
rescue Zlib::DataError => e
145+
raise RuntimeError, "Invalid compression"
146+
end
147+
end
148+
149+
@code
144150
end
145151
end
146152

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# -*- coding:binary -*-
2+
require 'spec_helper'
3+
4+
require 'rex/exploitation/powershell'
5+
6+
describe Rex::Exploitation::Powershell::Output do
7+
8+
let(:example_script) do
9+
Rex::Text.rand_text_alpha(400)
10+
end
11+
12+
let(:subject) do
13+
Rex::Exploitation::Powershell::Script.new(example_script)
14+
end
15+
16+
let(:eof) do
17+
Rex::Text.rand_text_alpha(10)
18+
end
19+
20+
describe "::to_s" do
21+
it 'should print the script' do
22+
subject.to_s.should eq example_script
23+
end
24+
end
25+
26+
describe "::size" do
27+
it 'should return the size of the script' do
28+
subject.size.should eq example_script.size
29+
end
30+
end
31+
32+
describe "::to_s_lineno" do
33+
it 'should print the script with line numbers' do
34+
subject.to_s_lineno.should eq "0: #{example_script}"
35+
end
36+
end
37+
38+
describe "::deflate_code" do
39+
it 'should zlib the code and wrap in powershell in uncompression stub' do
40+
compressed = subject.deflate_code
41+
compressed.include?('IO.Compression.DeflateStream').should be_true
42+
compressed =~ /FromBase64String\('([A-Za-z0-9\/+=]+)'\)/
43+
$1.size.should be < Rex::Text.encode_base64(example_script).size
44+
compressed.should eq subject.code
45+
end
46+
47+
it 'should append an eof marker if specified' do
48+
compressed = subject.deflate_code(eof)
49+
compressed.include?("echo '#{eof}';").should be_true
50+
end
51+
end
52+
53+
describe "::encode_code" do
54+
it 'should base64 encode the code' do
55+
encoded = subject.encode_code
56+
encoded.should eq subject.code
57+
encoded =~ /^([A-Za-z0-9\/+=]+)$/
58+
$1.size.should eq encoded.size
59+
end
60+
end
61+
62+
describe "::gzip_code" do
63+
it 'should gzip the code and wrap in powershell in uncompression stub' do
64+
compressed = subject.gzip_code
65+
compressed.include?('IO.Compression.GzipStream').should be_true
66+
compressed =~ /FromBase64String\('([A-Za-z0-9\/+=]+)'\)/
67+
$1.size.should be < Rex::Text.encode_base64(example_script).size
68+
compressed.should eq subject.code
69+
end
70+
71+
it 'should append an eof marker if specified' do
72+
compressed = subject.gzip_code(eof)
73+
compressed.include?("echo '#{eof}';").should be_true
74+
end
75+
end
76+
77+
describe "::compress_code" do
78+
it 'should gzip by default' do
79+
compressed = subject.compress_code
80+
compressed.include?('IO.Compression.GzipStream').should be_true
81+
end
82+
83+
it 'should deflate if gzip is false' do
84+
compressed = subject.compress_code(nil,false)
85+
compressed.include?('IO.Compression.DeflateStream').should be_true
86+
end
87+
88+
it 'should append an eof' do
89+
compressed = subject.compress_code(eof)
90+
compressed.include?("echo '#{eof}';").should be_true
91+
end
92+
end
93+
94+
describe "::decompress_code" do
95+
it 'should locate the base64 string and decompress it when deflate is used' do
96+
compressed = subject.compress_code(nil, false)
97+
decompressed = subject.decompress_code
98+
decompressed.should eq example_script
99+
end
100+
101+
it 'should locate the base64 string and decompress it when gzip is used' do
102+
compressed = subject.compress_code
103+
decompressed = subject.decompress_code
104+
decompressed.should eq example_script
105+
end
106+
107+
it 'should raise a RuntimeException if the Base64 string is not compressed/corrupted' do
108+
corrupted = "FromBase64String('parp')"
109+
subject.code = corrupted
110+
expect { subject.decompress_code }.to raise_error(RuntimeError)
111+
subject.code.should eq corrupted
112+
end
113+
end
114+
end
115+

0 commit comments

Comments
 (0)