Skip to content

在 Windows 后渗透模块中使用 Railgun

L edited this page May 10, 2022 · 1 revision

Railgun 是 Windows Meterpreter 独有的非常强大的后渗透利用功能. 它允许你完全控制目标机器的 Windows API, 或者你可以使用你找到的任何 DLL 并用它做更多创造性的事情. 例如: 假设你在 Windows 目标上有一个 Meterpreter 会话. 你关注的是你认为存储用户密码的特定应用程序, 但它是加密的, 并且没有用于解密的工具. 使用 Railgun, 你可以做的是, 你可以进入进程并 grep 查找内存中的任何敏感信息, 或者你可以查找负责解密的程序 DLL, 调用它, 然后让它为你解密. 如果你是一名渗透测试员, 显然后期利用是一项重要的技能, 但如果你不了解 Railgun, 那么你会错过很多.

定义 DLL 及其函数

Windows API 显然非常大, 因此默认情况下, Railgun 仅附带少量预定义的 DLL 和函数, 这些 DLL 和函数通常用于构建 Windows 程序. 这些内置 DLL 是: kernel32、ntdll、user32、ws2_32、iphlpapi、advapi32、shell32、netapi32、crypt32、wlanapi、wldap32、version. 也可以使用 known_dll_names 方法检索相同的内置 DLL 列表.

所有 DLL 定义都可以在 "def" 目录中找到, 它们被定义为类. 以下模板应演示如何实际定义 DLL:

# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Railgun
module Def

class Def_somedll

  def self.create_dll(dll_path = 'somedll')
    dll = DLL.new(dll_path, ApiConstants.manager)

    # 第一个参数 = 函数名
    # 第二个参数 = 返回值类型
    # 第三个参数 = 参数数组
    dll.add_function('SomeFunction', 'DWORD',[
      ["DWORD","hwnd","in"]
    ])

    return dll
  end

end

end; end; end; end; end; end; end

在函数定义中, Railgun 支持以下数据类型: VOID, BOOL, DWORD, WORD, BYTE, LPVOID, HANDLE, PDWORD, PWCHAR, PCHAR, PBLOB.

有四个参数/缓冲区方向: in, out, inout 和 return. 当你将值传递给"in"参数时, Railgun 会处理内存管理. 例如, MessageBoxA 有一个名为 lpText 的"in"参数, 并且是 PCHAR 类型. 你可以简单地向它传递一个 Ruby 字符串, 其余的由 Railgun 处理, 这一切都非常简单.

"out"参数将始终是指针数据类型. 基本上你告诉 Railgun 为参数分配多少字节, 它分配内存, 在调用函数时提供指向它的指针, 然后它读取函数写入的内存区域, 将其转换为 Ruby 对象并将其添加到返回哈希.

"inout"参数用作被调用函数的输入, 但可能会被它修改. 你可以像"out"参数一样检查修改后的值的返回哈希值.

在运行时定义新函数的快速方法可以像以下示例一样完成:

client.railgun.add_function('user32', 'MessageBoxA', 'DWORD',[
	["DWORD","hWnd","in"],
	["PCHAR","lpText","in"],
	["PCHAR","lpCaption","in"],
	["DWORD","uType","in"]
 ])

然而如果这个函数很可能会被多次使用, 或者它是 Windows API 的一部分, 那么你应该把它放在库中.

使用方法

尝试 Railgun 的最佳方法是在 Windows Meterpreter 提示符下使用 irb, 示例:

$ msfconsole -q
msf > use exploit/multi/handler
msf exploit(handler) > run

[*] Started reverse handler on 192.168.1.64:4444
[*] Starting the payload handler...
[*] Sending stage (769536 bytes) to 192.168.1.106
[*] Meterpreter session 1 opened (192.168.1.64:4444 -> 192.168.1.106:55148) at 2014-07-30 19:49:35 -0500

meterpreter > irb
[*] Starting IRB shell
[*] The 'client' variable holds the meterpreter client

>>

请注意, 当你运行 post 模块或在 irb 中时, 你总是有一个 clinetsession 对象可以使用, 它们都指向同一事物, 在本例中为 Msf::Sessions::Meterpreter_x86_Win. 此 Meterpreter 会话对象使你可以 API 访问目标机器, 包括 Railgun 对象 Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun. 以下是你如何简单地访问它:

session.railgun

如果你在 irb 中运行上面的代码, 你会看到它返回了关于所有 DLL、函数、常量等的信息, 除了它读起来有点不友好, 因为数据太多了. 幸运的是, 有一些方便的技巧可以帮助我们解决问题. 例如, 就像我们之前提到的, 如果你不确定加载了哪些 DLL, 你可以调用 known_dll_names 方法:

>> session.railgun.known_dll_names
=> ["kernel32", "ntdll", "user32", "ws2_32", "iphlpapi", "advapi32", "shell32", "netapi32", "crypt32", "wlanapi", "wldap32", "version"]

现在, 假设我们对 user32 感兴趣并且我们想要找到所有可用的函数 (以及返回值的数据类型、参数) , 另一个方便的技巧是:

session.railgun.user32.functions.each_pair {|n, v| puts "Function name: #{n}, Returns: #{v.return_type}, Params: #{v.params}"}

请注意, 如果你碰巧调用了无效或不受支持的 Windows 函数, 则会引发 RuntimeError, 并且错误消息还会显示可用函数的列表.

要调用 Windows API 函数, 方法如下:

>> session.railgun.user32.MessageBoxA(0, "hello, world", "hello", "MB_OK")
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>1}

如你所见, 此 API 调用返回一个哈希值. 我们看到的一个习惯是, 有时人们不喜欢检查 GetLastErrorErrorMessagereturn, 他们只是假设它有效. 这是一个不好的编程习惯, 不推荐. 如果你总是假设某事有效, 并执行下一个 API 调用, 则可能会出现意外结果 (最坏的情况: 丢失 Meterpreter 会话) .

内存读写

Railgun 类还有两个非常有用的方法, 你可能会用到: memreadmemwrite. 这些名称非常不言自明: 你读取了一块内存, 或者你写入了一个内存区域. 我们将在有效负载本身中使用一个新的内存块来演示这一点:

>> p = session.sys.process.open(session.sys.process.getpid, PROCESS_ALL_ACCESS)
=> #<#<Class:0x007fe2e051b740>:0x007fe2c5a258a0 @client=#<Session:meterpreter 192.168.1.106:55151 (192.168.1.106) "WIN-6NH0Q8CJQVM\sinn3r @ WIN-6NH0Q8CJQVM">, @handle=448, @channel=nil, @pid=2268, @aliases={"image"=>#<Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::Image:0x007fe2c5a25828 @process=#<#<Class:0x007fe2e051b740>:0x007fe2c5a258a0 ...>>, "io"=>#<Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::IO:0x007fe2c5a257b0 @process=#<#<Class:0x007fe2e051b740>:0x007fe2c5a258a0 ...>>, "memory"=>#<Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::Memory:0x007fe2c5a25738 @process=#<#<Class:0x007fe2e051b740>:0x007fe2c5a258a0 ...>>, "thread"=>#<Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::Thread:0x007fe2c5a256c0 @process=#<#<Class:0x007fe2e051b740>:0x007fe2c5a258a0 ...>>}>
>> p.memory.allocate(1024)
=> 5898240

如你所见, 新分配位于地址 5898240 (或十六进制的 0x005A0000) . 让我们首先向它写入四个字节:

>> session.railgun.memwrite(5898240, "AAAA", 4)
=> true

memwrite 返回 true, 表示成功. 现在让我们从 0x005A0000 读取 4 个字节:

>> session.railgun.memread(5898240, 4)
=> "AAAA"

请注意, 如果你提供了错误的指针, 你可能会导致访问冲突并使 Meterpreter 崩溃.

参考:

https://www.youtube.com/watch?v=AniR-T0AnnI

https://www.defcon.org/images/defcon-20/dc-20-presentations/Maloney/DEFCON-20-Maloney-Railgun.pdf

https://dev.metasploit.com/redmine/projects/framework/wiki/RailgunUsage

https://github.com/rapid7/metasploit-framework/tree/master/lib/rex/post/meterpreter/extensions/stdapi/railgun

http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx

http://msdn.microsoft.com/en-us/library/aa383749

http://undocumented.ntinternals.net/

http://source.winehq.org/WineAPI/

Clone this wiki locally