微信公众号:七夜安全博客 关注信息安全技术、关注 系统底层原理。问题或建议,请公众号留言。

一.前言

本篇聊一聊 新的主题:《反弹shell-逃逸基于execve的命令监控》,打算写一个专题,预估可以写三篇,内容确实有点多,也是最近研究了一些有意思的东西,想给大家分享一下。喜欢的话,请大家一定点在看,并分享出去,算是对我原创最大的支持了。

二.Shell命令监控

在linux中,大家用的比较多的是shell命令,同样在渗透到linux服务器,植入木马后,探测信息,执行恶意操作,维持权限,横向移动,shell命令也是必不可少的。既然在攻击侧,shell命令如此重要,那在安全防御方面,shell命令的监控也是非常关键的检测维度。各大厂商,一般怎么监控shell命令的调用呢?

2.1 基于execve的shell命令监控

系统命令,其实就是一个个程序,执行起来也就是一个个进程。命令执行的监控,也就是对外部进程创建的监控。在linux中,启动外部进程,是通过execve系统调用进行创建的,我们使用strace 打印一下在bash中启动ls的系统调用,第一句就是通过execve启动ls。

但是我们在开发linux程序的时候,执行系统命令,并没有直接使用execve系统调用,这是因为libc/glibc库对execve系统调用封装成了函数,方便我们调用。

因此基于execve的系统命令监控方式,分成了用户态和内核态。用户态通过劫持libc/glibc的exec相关函数来实现,内核态则通过系统自身组件或者劫持execve syscall 来实现。

1.用户态

在libc/glibc中,对execve syscall 进行了一系列的封装,简称exec族函数。exec系列函数调用时,启动新进程,替换掉当前进程。即程序不会再返回到原进程,具体内容如下:

int execl(constchar*path,constchar*arg0,...,(char*)0);

int execlp(constchar*file,constchar*arg0,...,(char*)0);

int execle(constchar*path,constchar*arg0,...,(char*)0,char*const envp[]);

int execv(cosnt char*path,char*const argv[]);

int execvp(cosnt char*file,char*const argv[]);

int execve(cosnt char*path,char*const argv[],char*const envp[]);

怎么劫持libc/glibc中的函数,我就不扩展了,大家google一下 so preload 劫持

2.内核态

在内核态监控其实是最准确,而且是最难绕过的。在内核态,一般是通过三种办法来监控:

  1. Netlink Connector

  2. Audit

  3. Hook execve syscall

(1)  Netlink Connector

在介绍 Netlink Connector 之前,首先了解一下 Netlink 是什么,Netlink 是一个套接字家族(socket family),它被用于内核与用户态进程以及用户态进程之间的 IPC 通信,ss命令就是通过 Netlink 与内核通信获取的信息。

Netlink Connector 是一种 Netlink ,它的 Netlink 协议号是NETLINK_CONNECTOR,其代码位于:

https://github.com/torvalds/linux/tree/master/drivers/connector

其中 connectors.c 和 cnqueue.c 是 Netlink Connector 的实现代码,而 cnproc.c 是一个应用实例名为进程事件连接器,我们可以通过该连接器来实现对进程创建的监控。

实现方案:

(引用来源:https://4hou.win/wordpress/?p=29586)

说明

  • linux内核提供连接器模块与进程事件收集机制,无需任何改动,只需要在linux>2.6.14开启即可。通过 cat/boot/config-$(uname-r)|egrep'CONFIGCONNECTOR|CONFIGPROC_EVENTS'就可以查看。

  • 在用户态实现轻量级ncp(netlink connector process)应用程序接收netlink进程事件消息

优点

轻量级,在用户态即可获得内核提供的信息。

缺点

仅能获取到 pid,详细信息需要查/proc/pid/,这就存在时间差,可能有数据丢失。

(2)  Audit

Audit 是 Linux 内核中用来进行审计的组件,可监控系统调用和文件访问,具体架构如下:

架构说明

  1. 用户通过用户态的管理进程配置规则(例如图中的 go-audit ,也可替换为常用的 auditd ),并通过 Netlink 套接字通知给内核。

  2. 内核中的 kauditd 通过 Netlink 获取到规则并加载。

  3. 应用程序在调用系统调用和系统调用返回时都会经过 kauditd ,kauditd 会将这些事件记录下来并通过 Netlink 回传给用户态进程。

  4. 用户态进程解析事件日志并输出。

优点

  • 组件完善,使用 auditd 软件包中的工具即可满足大部分需求,无需额外开发代码。

  • 相比于 Netlink Connector ,获取的信息更为全面,不仅仅是 pid 。

缺点

性能消耗随着进程数量提升有所上升。

(3)  Hook execve syscall

除了Netlink Connector 和 Audit 这两种Linux 本身提供的监控系统调用方式,如果想拥有更大程度的可定制化,就需要通过安装内核模块来对系统调用进行 hook。

目前常用的 hook 方法是通过修改syscall table(Linux 系统调用表)来实现,原理是系统在执行系统调用时是通过系统调用号在syscalltable中找到相应的函数进行调用,所以只要将syscalltable中execve对应的地址改为我们安装的内核模块中的函数地址即可.

具体细节请参考:驭龙 HIDS实现进程监控,里面已经介绍的非常详细了,不再赘述。

优点

高定制化,从系统调用层面获取完整信息。

缺点

  • 开发难度大,非常考验开发人员的技术功底。

  • 兼容性差,需针对不同发行版和内核版本进行定制和测试。

2.2 基于 Patch Shell解释器的命令监控

基于 Patch Shell解释器的命令监控是基于execve的系统命令监控的补充方案,因为通过监控execve系统调用的方式,理论上可以完全覆盖系统命令的调用,那为什么还要 Patch Shell解释器呢?

大家别忘了,shell不仅可以调用外部系统命令,自身还有很多内置命令。内置命令是shell解释器中的一部分,可以理解为是shell解释器中的一个函数,并不会额外创建进程。因此监控execve系统调用是无法监控这部分的,当然能用作恶意行为的内置命令并不多,算是一个补充。

如何判断是否是内置命令呢?通过type指令,示例如下:

[root@localhost ~]# type cd

cd is a Shell builtin

[root@localhost ~]# type ifconfig

ifconfig is/sbin/ifconfig

完整的内置命令列表,请参考 shell内置命令[http://c.biancheng.net/view/1136.html]。

如何Patch Shell解释器 ? 原理很简单,对shell解释器的输入进行修改,将输入写入到文件中,进行分析即可。shell解释器很多,以bash举例:

  1. 通过 -c 参数输入命令

  2. 通过stdin输入命令。

在这两个地方将写文件的代码嵌入进去即可。

三.已知对抗Shell命令监控方法

以上讲解了现有Shell命令监控方法,下面一一进行击破。对抗命令监控一般是在三个方面动手脚:

  1. 绕过Shell命令监控方法,使之收集不到命令执行的日志。

  2. 无法绕过命令监控,但是能篡改命令执行的进程和参数,使之收集到假的日志

  3. 无法绕过监控,也无法篡改内容, 猜测命令告警的策略并绕过(例如通过混淆绕过命令静态检测)

在上述的三个方法中,第一种和第二种方法算是比较根本的方法,没有真实的数据,策略模型就无法命中目标并告警,第三种方法需要较多的经验,但是通过混淆命令绕过静态检测策略,也是比较常见的。

3.1 无日志-绕过Shell命令监控

已知的绕过命令监控的方案:用户态glibc/libc exec劫持,Patch Shell解释器,内核态的execve监控,均可被绕过。

1. 绕过glibc/libc exec劫持

方法1:glibc/libc是linux中常用的动态链接库,也就是说在动态链接程序的时候才会用到它,那么我们只需要将木马后门进行静态编译即可,不依赖系统中的glibc/libc执行,就不会被劫持。

方法2: glibc/libc是对linux系统调用(syscall)的封装,我们使用它是为了简化对系统调用的使用,其实我们可以不用它,直接使用汇编 sysenter/int 0x80指令调用execve系统调用,下面是使用int 0x80调用execve syscall的简写代码:

mov byte al, 0x0b # 好了,现在把execve()的系统调用号11号放入eax的最下位的al中

mov ebx, esi # 现在是第一个参数,字符串的位置放入ebx

lea ecx, [esi+8] # 第二个参数,要点是这个参数类型是char **, 如果/bin/sh有其它参数的话,整个程序写法就又不一样了

lea edx, [esi+12] # 最后是null的地址,注意,是null的地址,不是null,因为写这是为了shellcode做准备,shellcode中不可以有null

int0x80 

当然还有其他方法,比如重写LD_PRELOAD环境变量,这样的动作太大,就不讲了。

2. 绕过Patch Shell解释器

方法1:不使用shell解释器执行命令,直接使用execve 方法2:不使用被Patch的shell解释器,例如大家常用的bash被patch,那你可以使用linux另一个 tcsh解释器来执行命令。

[root@VM_0_13_centos ~]# tcsh -c "echo hello"

hello

3.绕过内核态execve syscall

只要你使用了execve执行了命令,就绝对逃不过内核态execve syscall的监控,太底层了,除非你把防御方的内核驱动给卸载了。既然如此,那怎么绕过呢?

方法很简单,就是不使用execve系统调用。(不是废话)

大家想想为什么会有反弹shell? 为什么要弹shell?

其实是我们想借用linux中自带的系统命令来达到我们的目的,尤其是在linux中以系统命令操作为主。

以 ls 命令为例子,功能是查看目录中有哪些文件,假如我们不想使用ls命令,那我们有什么办法呢?

那就自己写一个类似功能程序的代码,然后执行就可以了。既然不想使用execve启动进程来执行,那直接在木马中执行shellcode就ok了。

我以python shellcode为例子(你也可以写 汇编 shellcode):

ls_shellcode = '''

import os

dst_path = '{dst_path}'

dirs = os.listdir(dst_path)

for file in dirs:

print(file)

'''

exec(ls_shellcode.format(dst_path = "C:/"))

输出:

$Recycle.Bin

DocumentsandSettings

Intel

pagefile.sys

PerfLogs

ProgramFiles

ProgramFiles(x86)

......

这样根本不会出现execve系统调用,你要把shellcode通过网络传输过来即可。

隐秘还是挺隐秘的,缺点就是费事,尤其是写汇编shellcode的时候,linux中使用的命令还是挺多的,而且自己写的shellcode,也没有原始linux命令使用的亲切感。

3.2 假日志 - 混淆进程名与进程参数

1.混淆进程名

在linux中有个syscall,名字叫做memfd_create (http://man7.org/linux/man-pages/man2/memfd_create.2.html)。

memfd_create()会创建一个匿名文件并返回一个指向这个文件的文件描述符.这个文件就像是一个普通文件一样,所以能够被修改,截断,内存映射等等.不同于一般文件,此文件是保存在RAM中.一旦所有指向这个文件的连接丢失,那么这个文件就会自动被释放

这就是说memfd_create创建的文件是存在与RAM中,那这个的文件名类似 /proc/self/fd/%d,也就是说假如我们把 ls命令bin文件使用memfd_create写到内存中,然后在内存中使用execve执行,那看到的不是 ls,而是执行的 /proc/self/fd/%d ,从而实现了进程名称混淆 和无文件。

具体看这篇文章(http://www.polaris-lab.com/index.php/archives/666/),非常详细,还有例子说明。

2.混淆进程参数

使用的是linux中另一个syscall: ptrace。ptrace是用来调试程序用的,使用execve启动进程,相对于自身来说是启动子进程,ptrace 的使用流程一般是这样的:

父进程 fork() 出子进程,子进程中执行我们所想要 trace 的程序,在子进程调用 exec() 之前,子进程需要先调用一次 ptrace,以 PTRACETRACEME 为参数。这个调用是为了告诉内核,当前进程已经正在被 traced,当子进程执行 execve() 之后,子进程会进入暂停状态,把控制权转给它的父进程(SIGCHLD信号), 而父进程在fork()之后,就调用 wait() 等子进程停下来,当 wait() 返回后,父进程就可以去查看子进程的寄存器或者对子进程做其它的事情了

假如我们想执行 ls-alh,在上文中 ls 已经可以被混淆了。接下来使用ptrace 对 -alh进行混淆。大体的操作流程如下:

  • 第一步:首先我们fork出来一个子进程,然后在子进程中先调用ptrace,接着执行execve("ls xxxxxx"),这个时候基于execve监控到的就是一个假参数

  • 第二步:既然传入的是假参数,那肯定是是无法执行到想要的结果,这个时候父进程等待子进程execve后停下来,然后修改传入参数的寄存器,将其修改为 -alh,最后接着让子进程继续运行即可。

具体请看这篇文章:http://www.polaris-lab.com/index.php/archives/667/,不再赘述。

四.新方法-无"命令"反弹shell

在已知的绕过方法中,通过shellcode方式绕过内核态的execve监控,算是相对优雅的方式了,我比较喜欢这种,但是这种方式又太麻烦,linux的命令我都要重写成shellcode, 而且显示方式肯定没有原来这么可爱

主要是懒。。。。

其实我的需求很简单:

我既想要linux命令原有的功能,又不想用execve syscall的方式启动

想了想,怎么办呢?

暂时不写了,删了一次,有点敏感,还望见谅

最后

这篇文章写了三个星期,主要是工作挺忙了,每天写一点,后台也有朋友经常催更的,很抱歉了。

最近工作方面也取得一些短暂的进展,运气还是会倾向于努力的人

继续战斗,敬请期待。

参考文献:

http://www.polaris-lab.com/index.php/archives/667/;

https://segmentfault.com/a/1190000019828080

推荐阅读:

漫谈威胁建模下的安全通信

脚本代码混淆-Python篇-pyminifier(2)

脚本代码混淆-Python篇-pyminifier(1)

APT组织武器:MuddyC3泄露代码分析

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

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

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

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

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

回复【8】:领取 python神经网络 教程 

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

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

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

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

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

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

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

shell 封装方法_反弹shell逃逸基于execve的命令监控(上)相关推荐

  1. centos7输入shell找不到命令_反弹shell原理与实现

    什么是反弹shell? 反弹shell(reverse shell),就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端.reverse shell与tel ...

  2. msf监听php反弹shell,使用msf进行反弹shell+内网渗透

    首先说明一点,反弹shell的前提是该主机可以执行咱们的恶意命令 反弹shell的原理与灰鸽子的原理是类似的,就是在靶机上执行一个咱们msf精心构造的客户端,使靶机主动回连咱们攻击机的某个端口.下面开 ...

  3. shell不允许输入空字符_反弹shell | ncamp;bash

    01 nc netcat,简称nc,一款TCP/UDP网络连接的利器,可实现任意TCP/UDP端口的侦听,被称为"瑞士军刀",可见其功能强大. nc的选项较多,这里只介绍我们日常工 ...

  4. 实践一:mipsel-栈溢出漏洞_开启telnet服务_反弹shell

    保护信息检查 没有开启NX .PIE等保护 漏洞分析 ida 分析: # 栈分析 .text:00400930 var_244 = -0x244 .text:00400930 var_242 = -0 ...

  5. linux 反弹shell(二)反弹shell的本质

    0X00 前言 在上一篇文章 Linux反弹shell(一)文件描述符与重定向,我们已经讨论过了反弹shell中最核心也是相对较难理解的部分,那么接下来我们就可以正式借反弹shell的实例分析回顾前一 ...

  6. python 网盘上传_【Python3】基于文叔叔网盘上传与下载的Python脚本

    [Python] 纯文本查看 复制代码import requests import os import hashlib from threading import Thread import sys ...

  7. shell 不等于_关于shell编程中的整数值比较的两种方式的简单操作实例

    谈一谈关于shell编程中的整数值比较的两种方式 Shell编程有时处理一个对象时,需要我们对对象进行测试. 只有符合要求的才采取下一步操作,这样做的好处可以避免程序出错. 这个测试的对象可以是文件. ...

  8. shell 打印追加_[转]shell 数组定义、使用和追加

    1.声明数组 declare -a myarray 在函数中还可以用local 来声明数组 local -a myarray 2.给数组赋值. 2.1.对于shell能返回多个值的,可以直接赋值,比如 ...

  9. python shell如何打开_“python shell怎么打开“python shell启动教程

    python shell怎么打开 1.简介:如何在python中运行shell(bash命令) 2.工具/原料:python库:os.py 3.方法:import os command = 'date ...

  10. 用shell打印正三角形_用shell命令绘制三角形

    本文旨在通过几个经典的图案来练习shell编程,涉及知识点:for循环,大小比较,基本的数学公式计算,echo小技巧.update:2019-10-17 10:13:54 初次绘制 $ for ((l ...

最新文章

  1. 计算机专业中专排名,成都计算机中专学校排名
  2. Ubuntu10.10源
  3. Spring mvc 3.0 入门及应用
  4. Centos7作为VNCserver,本地使用VNCViewer连接
  5. hive复合数据类型之map
  6. python脚本运行命令_从Python脚本运行shell命令
  7. 键盘上在方向键上面的9个键是干什么的?
  8. 刚刚,中国估值最高的AI公司一口气发布11款产品,横跨5大领域
  9. PyCharm 专题
  10. presumably用法
  11. 07《基于深度学习的车标识别方法研究》学习总结
  12. libreelec投屏_PVE系列四:安装LibreELEC-KODI的LINUX版
  13. 第十六届全国大学生智能车竞赛线上比赛监督裁判培训演练
  14. android控件显示在最上层,「总是可见的时钟和备忘录」永远显示在屏幕最上层的贴心助手(Android)...
  15. 内容管理系统CMS简介
  16. OllyDBG破解入门教程[图文]教程
  17. Yolov3模型使用教程
  18. 二手车 电商+互联网金融的三种新玩法
  19. 【参赛作品31】ODBC驱动连接MogDB/openGauss
  20. 树形控件--zTree 很好很强大

热门文章

  1. 【Mybatis】Mybatis三大组件之ResultSetHandler
  2. 【Spring-tx】关于spring事务的思考
  3. Java 对象布局、大小工具 jar包工具类jol
  4. Maven搭建SpringMVC项目详解
  5. nrf52832-定时器例程
  6. Hibernate一对多双向、单向
  7. AIX下内存泄漏的监控
  8. 安装phpDocumentor
  9. 获取Spring容器中Bean实例的工具类(Java泛型方法实现)
  10. 关于C#操作mysql数据库乱码