What what what, Post-Exploitation attack!

git-svn-id: file:///home/svn/incoming/trunk@2334 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Spoon M 2005-04-03 21:52:10 +00:00
parent 87e3fda1fc
commit c5a1fe6716
11 changed files with 700 additions and 0 deletions

3
lib/rex/post.rb Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/ruby
require 'Rex/Post/DispatchNinja'

View File

@ -0,0 +1,3 @@
#!/usr/bin/ruby
require 'Rex/Post/DispatchNinja/Client.rb'

View File

@ -0,0 +1,77 @@
#!/usr/bin/ruby
require 'Rex/Post/DispatchNinja/File'
require 'Rex/Post/DispatchNinja/FileStat'
require 'Rex/Post/DispatchNinja/Process'
module Rex
module Post
module DispatchNinja
class Client
private
attr_accessor :sock
public
def initialize(sock)
self.sock = sock
checksig()
end
def file
klass = Rex::Post::DispatchNinja::File.dup
klass.client = self
return klass
end
def filestat
klass = Rex::Post::DispatchNinja::FileStat.dup
klass.client = self
return klass
end
def process
klass = Rex::Post::DispatchNinja::Process.dup
klass.client = self
return klass
end
def sendmodule(name)
name = 'lib/Rex/Post/DispatchNinja/modules/' + name
data = IO.readlines(name, '')[0]
sockwrite([data.length].pack('V'))
sockwrite(data)
end
def checksig
if !select( [ sock ], nil, nil, 2)
puts "Possible sync problem?"
else
sig = sockread(4)
if sig != "AAAA"
puts "Sig #{sig} didn't match"
end
end
end
def sendfilename(dir)
dir += "\x00" # null terminate filename for easy syscall
sockwrite( [ dir.length ].pack('V') )
sockwrite(dir)
end
# do true full read/write, blocking. So if I say read 4 bytes, I'll
# block until I get all 4 bytes
def sockwrite(data)
sock.write(data)
end
def sockread(len)
return sock.read(len)
end
end
end; end; end # DispatchNinja/Post/Rex

View File

@ -0,0 +1,117 @@
#!/usr/bin/ruby
require 'Rex/DispatchNinja/Stat'
module Rex
module Post
module DispatchNinja
class File < Rex::Post::File
#
# Class Methods
#
@@structstat = [
'st_dev', 2,
'pad1', 2,
'st_ino', 4,
'st_mode', 2,
'st_nlink', 2,
'st_uid', 2,
'st_gid', 2,
'st_rdev', 2,
'pad2', 2,
'st_size', 4,
'st_blksize', 4,
'st_blocks', 4,
'st_atime', 4,
'unused1', 4,
'st_mtime', 4,
'unused2', 4,
'st_ctime', 4,
'unused3', 4,
'unused4', 4,
'unused5', 4
]
# setup a class variable for our client pointer
class <<self
attr_accessor :client
end
def File.stat(file)
return client.filestat.new(file)
end
def ls(dir)
sendmodule('ls')
sendfilename(dir)
res = sockread(4).unpack('l')[0] # ug, not portable, later...
files = [ ]
while true
len = sockread(2).unpack('S')[0]
break if len == 0
files << sockread(len)
end
checksig()
return [ res, files ]
end
def File.stat_hash(file)
client.sendmodule('stat')
client.sendfilename(file)
data = client.sockread(68)
res = data[0, 4].unpack('l')[0]
# throw exception! blah!
client.checksig()
data = data[4 .. -1]
elements = @@structstat
hash = { }
i = 0
o = 0
while i < elements.length
name = elements[i]
size = elements[i + 1]
i += 2
e = data[o, size].unpack(size == 2 ? 'S' : 'L')[0]
o += size
hash[name] = e
end
return(hash)
end
#
# Instance Methods
#
# setup an instance variable, just for ease and copy it over..
# and so you can change it instance wise
private
attr_accessor :client
public
def initialize()
self.client = self.class.client
end
end
end; end; end # DispatchNinja/Post/Rex

View File

@ -0,0 +1,37 @@
#!/usr/bin/ruby
require 'Rex/Post/File'
module Rex
module Post
module DispatchNinja
class File < Rex::Post::File
# setup a class variable for our client pointer
class <<self
attr_accessor :client
end
def File.stat(name)
client.filestat.new(name)
end
def File.stat_data(file)
client.sendmodule('stat')
client.sendfilename(file)
data = client.sockread(68)
res = data[0, 4].unpack('l')[0]
# throw exception! blah!
client.checksig()
return data[4 .. -1]
end
end
end; end; end # DispatchNinja/Post/Rex

View File

@ -0,0 +1,59 @@
#!/usr/bin/ruby
require 'Rex/Post/FileStat'
module Rex
module Post
module DispatchNinja
class FileStat < Rex::Post::FileStat
@@structstat = [
'st_dev', 2,
'pad1', 2,
'st_ino', 4,
'st_mode', 2,
'st_nlink', 2,
'st_uid', 2,
'st_gid', 2,
'st_rdev', 2,
'pad2', 2,
'st_size', 4,
'st_blksize', 4,
'st_blocks', 4,
'st_atime', 4,
'unused1', 4,
'st_mtime', 4,
'unused2', 4,
'st_ctime', 4,
'unused3', 4,
'unused4', 4,
'unused5', 4
]
class <<self
attr_accessor :client
end
def initialize(file)
self.stathash = parse_struct_stat(self.class.client.file.stat_data(file))
end
def parse_struct_stat(data)
elements = @@structstat
hash = { }
i = 0
o = 0
while i < elements.length
name = elements[i]
size = elements[i + 1]
i += 2
e = data[o, size].unpack(size == 2 ? 'S' : 'L')[0]
o += size
hash[name] = e
end
return(hash)
end
end
end; end; end # DispatchNinja/Post/Rex

View File

@ -0,0 +1,28 @@
#!/usr/bin/ruby
require 'Rex/Post/Process'
module Rex
module Post
module DispatchNinja
class Process < Rex::Post::Process
class <<self
attr_accessor :client
end
def Process.getresuid()
# gotta fix this, getresuid could fail
# I don't transfer the return value on the wire...
client.sendmodule('getresuid')
data = client.sockread(12)
client.checksig()
return data.unpack('l3')
end
end
end; end; end # DispatchNinja/Post/Rex

120
lib/rex/post/file.rb Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/ruby
module Rex
module Post
class File
#
# Class Methods
#
# setup a class variable for our client pointer
# class <<self
# attr_accessor :client
# end
# def File.stat(file)
# return client.filestat.new(file)
# end
# def File.stat_hash(file)
# raise NotImplementedError
# end
#
# autogen'd stat passthroughs
#
def File.atime(name)
stat(name).atime
end
def File.blockdev?(name)
stat(name).blockdev?
end
def File.chardev?(name)
stat(name).chardev?
end
def File.ctime(name)
stat(name).ctime
end
def File.directory?(name)
stat(name).directory?
end
def File.executable?(name)
stat(name).executable?
end
def File.executable_real?(name)
stat(name).executable_real?
end
def File.file?(name)
stat(name).file?
end
def File.ftype(name)
stat(name).ftype
end
def File.grpowned?(name)
stat(name).grpowned?
end
def File.mtime(name)
stat(name).mtime
end
def File.owned?(name)
stat(name).owned?
end
def File.pipe?(name)
stat(name).pipe?
end
def File.readable?(name)
stat(name).readable?
end
def File.readable_real?(name)
stat(name).readable_real?
end
def File.setuid?(name)
stat(name).setuid?
end
def File.setgid?(name)
stat(name).setgid?
end
def File.size(name)
stat(name).size
end
def File.socket?(name)
stat(name).socket?
end
def File.sticky?(name)
stat(name).sticky?
end
def File.symlink?(name)
stat(name).symlink?
end
def File.writeable?(name)
stat(name).writeable?
end
def File.writeable_real?(name)
stat(name).writeable_real?
end
def File.zero?(name)
stat(name).zero?
end
#
# Instance Methods
#
# setup an instance variable, just for ease and copy it over..
# and so you can change it instance wise
# private
# attr_accessor :client
# public
# def initialize()
# self.client = self.class.client
# end
end
end; end # Post/Rex

198
lib/rex/post/file_stat.rb Normal file
View File

@ -0,0 +1,198 @@
#!/usr/bin/ruby
#
# This is just a container class basically, that acts like File::Struct
#
# You must supply an initialize method that somehow populates the stathash..
#
module Rex
module Post
class FileStat
@@ftypes = [
'fifo', 'characterSpecial', 'directory',
'blockSpecial', 'file', 'link', 'socket'
]
# class <<self
# attr_accessor :client
# end
private
attr_accessor :stathash
public
# def initialize(file)
# self.stathash = self.class.client.file.stat_hash(file)
# end
def dev
stathash['st_dev']
end
def ino
stathash['st_ino']
end
def mode
stathash['st_mode']
end
def nlink
stathash['st_nlink']
end
def uid
stathash['st_uid']
end
def gid
stathash['st_gid']
end
def rdev
stathash['st_rdev']
end
def size
stathash['st_size']
end
def blksize
stathash['st_blksize']
end
def blocks
stathash['st_blocks']
end
def atime
Time.at(stathash['st_atime'])
end
def mtime
Time.at(stathash['st_mtime'])
end
def ctime
Time.at(stathash['st_ctime'])
end
#
# S_IFMT 0170000 bitmask for the file type bitfields
# S_IFSOCK 0140000 socket
# S_IFLNK 0120000 symbolic link
# S_IFREG 0100000 regular file
# S_IFBLK 0060000 block device
# S_IFDIR 0040000 directory
# S_IFCHR 0020000 character device
# S_IFIFO 0010000 fifo
#
# this is my own, just a helper...
def filetype?(mask)
return true if mode & 0170000 == mask
return false
end
def blockdev?
filetype?(060000)
end
def chardev?
filetype?(020000)
end
def directory?
filetype?(040000)
end
def file?
filetype?(0100000)
end
def pipe?
filetype?(010000) # ??? fifo?
end
def socket?
filetype(0140000)
end
def symlink?
filetype(0120000)
end
def ftype
return @@ftypes[(mode & 0170000) >> 13].dup
end
#
# S_ISUID 0004000 set UID bit
# S_ISGID 0002000 set GID bit (see below)
# S_ISVTX 0001000 sticky bit (see below)
# S_IRWXU 00700 mask for file owner permissions
# S_IRUSR 00400 owner has read permission
# S_IWUSR 00200 owner has write permission
# S_IXUSR 00100 owner has execute permission
# S_IRWXG 00070 mask for group permissions
# S_IRGRP 00040 group has read permission
# S_IWGRP 00020 group has write permission
# S_IXGRP 00010 group has execute permission
# S_IRWXO 00007 mask for permissions for others (not in group)
# S_IROTH 00004 others have read permission
# S_IWOTH 00002 others have write permisson
# S_IXOTH 00001 others have execute permission
#
def perm?(mask)
return true if mode & mask == mask
return false
end
def setgid?
perm?(02000)
end
def setuid?
perm?(04000)
end
def sticky?
perm?(01000)
end
def executable?
raise NotImplementedError
end
def executable_real?
raise NotImplementedError
end
def grpowned?
raise NotImplementedError
end
def owned?
raise NotImplementedError
end
def readable?
raise NotImplementedError
end
def readable_real?
raise NotImplementedError
end
def writeable?
raise NotImplementedError
end
def writeable_real?
raise NotImplementedError
end
def prettymode
m = mode
om = '%04o' % m
perms = ''
3.times {
perms = ((m & 01) == 01 ? 'x' : '-') + perms
perms = ((m & 02) == 02 ? 'w' : '-') + perms
perms = ((m & 04) == 04 ? 'r' : '-') + perms
m >>= 3
}
return "#{om}/#{perms}"
end
def pretty
" Size: #{size} Blocks: #{blocks} IO Block: #{blksize} Type: #{rdev}\n"\
"Device: #{dev} Inode: #{ino} Links: #{nlink}\n"\
" Mode: #{prettymode}\n"\
" Uid: #{uid} Gid: #{gid}\n"\
"Access: #{atime}\n"\
"Modify: #{mtime}\n"\
"Change: #{ctime}\n"
end
end
end; end # Post/Rex

13
lib/rex/post/gen.pl Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/perl
use strict;
foreach my $f ('atime', 'blockdev?', 'chardev?', 'ctime', 'directory?',
'executable?', 'executable_real?', 'file?', 'ftype', 'grpowned?',
'mtime', 'owned?', 'pipe?', 'readable?', 'readable_real?', 'setuid?',
'setgid?', 'size', 'socket?', 'sticky?', 'symlink?', 'writeable?',
'writeable_real?', 'zero?') {
my $t = "\t";
print "${t}def File.$f(name)\n\t${t}stat(name).$f\n${t}end\n";
}

45
lib/rex/post/process.rb Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/ruby
module Rex
module Post
class Process
private_class_method :new
def Process.getresuid
raise NotImplementedError
end
def Process.setresuid(a, b, c)
raise NotImplementedError
end
def Process.euid
getresuid()[1]
end
def Process.euid=(id)
setresuid(-1, id, -1)
end
def Process.uid
getresuid()[0]
end
def Process.uid=(id)
setresuid(id, -1, -1)
end
def Process.egid
getresgid()[1]
end
def Process.egid=(id)
setresgid(-1, id, -1)
end
def Process.gid
getresgid()[0]
end
def Process.gid=(id)
setresgid(id, -1, -1)
end
end
end; end # Post/Rex