pwnable.kr小白題解

題目一(fd - 1 pt )

打開題目,使用SSH鏈接到目標服務器。發(fā)現(xiàn)有三個文件分別為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的第一參數(shù)可控,查閱資料可得(以下內容摘自https://www.cnblogs.com/xiehongfeng100/p/4619451.html):

在Linux下read函數(shù)定義如下:

#include <unistd>
ssize_t read(int filedes, void *buf, size_t nbytes);
// 返回:若成功則返回讀到的字節(jié)數(shù),若已到文件末尾則返回0,若出錯則返回-1
// filedes:文件描述符[0-標準輸入(stdin),1-標準輸出(stdout),2-標準錯誤輸出(stderr)]
// buf:讀取數(shù)據緩存區(qū)
// nbytes:要讀取的字節(jié)數(shù)

有幾種情況可使實際讀到的字節(jié)數(shù)少于要求讀的字節(jié)數(shù):

1)讀普通文件時,在讀到要求字節(jié)數(shù)之前就已經達到了文件末端。例如,若在到達文件末端之前還有30個字節(jié),而要求讀100個字節(jié),則read返回30,下一次再調用read時,它將返回0(文件末端)。

2)當從終端設備讀時,通常一次最多讀一行。

3)當從網絡讀時,網絡中的緩存機構可能造成返回值小于所要求讀的字結束。

4)當從管道或FIFO讀時,如若管道包含的字節(jié)少于所需的數(shù)量,那么read將只返回實際可用的字節(jié)數(shù)。

5)當從某些面向記錄的設備(例如磁帶)讀時,一次最多返回一個記錄。

6)當某一個信號造成中斷,而已經讀取了部分數(shù)據。

在《UNIX網絡編程 卷1》中,作者將該函數(shù)進行了封裝,以確保數(shù)據讀取的完整,具體程序如下:

 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函數(shù)的filedes(文件描述符)為0即可對buf變量進行最大32字節(jié)的填充,這里只需要填充"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字節(jié)長度的參數(shù),經過check_password的變換,輸出int變量與0x21DD09EC比較,相等即輸出flag。

為了說明數(shù)據在內存中的存儲方式,在這里我們用一個簡單的方式給予說明。

#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;
}

發(fā)現(xiàn)輸出

1145258561
1212630597
1280002633
1347374669
5460561

--------------------------------
Process exited with return value 0
Press any key to continue . . .

轉換為16進制發(fā)現(xiàn)即為

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 . . .

顯然正確,那么嘗試帶參數(shù)運行程序。

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

拿到flag。

題目三(bof - 5 pt )

等待更新~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容