Custom Shellcode Crypter
Objectives
- Create a custom crypter
- Free to use any existing encryption schema
- Use any programming language
Crypter
Another process to conceal a shellcode by means of encrypting with different schema. “Encryption is the foundation of modern security.” as snowden mentioned. In case of exploit development, it is another technique such as Encoding & Polymorphism from previous assignments to make detection harder.
Advanced Encryption Standard (AES)
The AES is originally Rijndael cipher, the winner of the NIST’s AES selection, is widely used. It can be implemented with 128, 192, or 256 bits of key.
The algorithm consist of several steps: sub bytes, shift rows, mix columns, & add round key. More details of the process here: https://formaestudio.com/rijndaelinspector/archivos/Rijndael_Animation_v4_eng-html5.html.
Fortunately, AES can be implemented using the OpenSSL
gem in Ruby. With the Cipher
class, AES with 256-bit key can be intantiated by:
OpenSSL::Cipher.new('AES-256-CBC')
Encryption and decryption are possible by passing the method .encrypt
and .decrypt
into the instance as decribed in the documentation. Also, a key is passed using the .key
method. But before passing the key, it can be processed further to make it 256-bit. OpenSSL’s Digest
class is capable of creating a 256-bit hash from an input data by using its .digest
method.
Ruby Script
Below Ruby script used the mentioned functionalities above. Comments are added to explain the functionality of the codes used.
The script have three options in processing the shellcode: encrypt only, decode only, or decode & execute.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
| #!/usr/bin/env ruby
# Author: Jason Villaluna
require 'optparse'
require "openssl"
def run
parse_args
validate_inputs
process_shellcode
rescue OptionParser::MissingArgument
puts "\e[33m[!] Missing argument:\e[0m"
@parser.parse %w[--help]
rescue StandardError => e
puts "\e[31m[-]\e[0m Encountered: \e[31m'#{e.class}' #{e}\e[0m"
exit(1)
end
# Simple check to validate cli arguments
def validate_inputs
messages = [
"Please provide both shellcode and key",
"Please choose one from encrypt, decrypt_only, and decrypt_run"
]
messages.shift if [@shellcode, @key].all? { |arg| arg }
messages.pop if ['encrypt', 'decrypt_only', 'decrypt_run'].include?(@execute)
return if messages.empty?
messages.each { |message| puts "\e[33m[!] #{message}\e[0m" }
exit(1)
end
# Choose which method to execute depending on the user's options
def process_shellcode
case @execute
when /^encrypt$/i
encrypt_shellcode
when /^decrypt_only$/i
decrypt_only
when /^decrypt_run$/i
decrypt_run
end
end
# Encrypt the shellcode with AES-256-CBC
def encrypt_shellcode
encrypted_shellcode = aes(@key, @shellcode) { |aes| aes.encrypt }
puts "\e[32m[+] Encrypted successfully:\e[0m "\
"'#{encrypted_shellcode.to_s.unpack('C*').map { |c| '\x%02x' % c }.join}'"
end
# Decrypt the encrypted shellcode
def decrypt_shellcode
shellcode = [@shellcode.split('\\x').join].pack('H*')
aes(@key, shellcode) { |aes| aes.decrypt }
end
# Displays the decrypted shellcode in stdout
def decrypt_only
decrypted_shellcode = decrypt_shellcode
puts "\e[32m[+] Decrypted successfully:\e[0m '#{decrypted_shellcode}'"
end
# Decrypt the shellcode, compile using 'shell.c', and then run
def decrypt_run
@decrypted_shellcode = decrypt_shellcode
replace_shellcode
compile_binary
run_binary
end
# AES-256-CBC encryption and decryption
# depending on what code is pass in the block
def aes(key, data)
aes = OpenSSL::Cipher.new('AES-256-CBC')
yield(aes)
aes.key = OpenSSL::Digest.digest("SHA256", key)
aes.update(data) + aes.final
end
# Replace a value inside a file
def replace_content(filename)
file_content = File.read(filename)
yield(file_content)
File.write(filename, file_content)
end
# Replace shellcode in the shellcode tester program
def replace_shellcode
replace_content('./shellcode.c') do |c_code|
c_code.gsub!(/\"\S+\"\;/, "\"#{@decrypted_shellcode}\"\;")
end
end
# Compile the shellcode into binary with the help of the shellcode.c program
def compile_binary
`gcc -fno-stack-protector -m32 -z execstack shellcode.c -o shellcode 2> /dev/null`
puts "\n\e[32m[+]\e[0m Successfully generated \e[32mshellcode\e[0m "
end
# Execute the shellcode binary
def run_binary
system('./shellcode')
end
def parse_args
@parser = OptionParser.new do |opts|
opts.banner = "Usage: #{$0} -s [shellcode] -k [key] -[e|d|r]"
opts.separator ""
opts.separator "Options:"
opts.on("--shellcode", "-s SHELLCODE", "Input Shellcode") do |value|
@shellcode = value
end
opts.on("--key", "-k KEY", "Pass key") do |value|
@key = value
end
opts.on(
"--execute", "-e [encrypt|decrypt_only|decrypt_run]",
"\n\t\tencrypt: displays encrypted shellcode"\
"\n\t\tdecrypt_only: displays decrypted shellcode:"\
"\n\t\tdecryp_run: decrypt, compile, and then run the shellcode"
) do |value|
@execute = value&.downcase
end
opts.on_tail("--help", "-h", "Print options") do |show_help|
warn opts
exit(0)
end
end
@parser.parse!
end
if __FILE__ == $PROGRAM_NAME
run
end
|
Script usage and testing
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE - 1558
Link to Github repository
| Link to index page