1. 程式人生 > >pwnable.kr input解題記錄

pwnable.kr input解題記錄

pwnable input解題記錄

給了原始碼如下:

#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "arpa/inet.h"

int main(){

    //stage argv
    char *argv[101] = {"/home/input2/input", [1 ... 99] = "A", NULL};
    argv['A'] = "\x00";
    argv['B'] = "\x20\x0a\x0d";
    argv['C'] = "55555";

    //stage stdio
    int pipe2stdin[2] = {-1, -1};
    int pipe2stderr[2] = {-1, -1};
    pid_t childpid;

    //stage file
    FILE* fp = fopen("\x0a", "w");
    fwrite("\x00\x00\x00\x00", 4, 1, fp);
    fclose(fp);

    if(pipe(pipe2stdin) < 0 || pipe(pipe2stderr) < 0)
    {
        perror("Cannot create the pipe!");
        exit(1);
    }
    if((childpid = fork()) < 0)
    {
        perror("Cannot fork!");
        exit(1);
    }
    if(childpid == 0)
    {
        close(pipe2stdin[0]);  //close pipes of read
        close(pipe2stderr[0]);
        
        write(pipe2stdin[1], "\x00\x0a\x00\xff", 4);
        write(pipe2stderr[1], "\x00\x0a\x02\xff", 4);
    }
    else{
        close(pipe2stdin[1]); close(pipe2stderr[1]);   //close pipes of write
        dup2(pipe2stdin[0], 0); dup2(pipe2stderr[0], 2);
        close(pipe2stdin[0]); close(pipe2stderr[0]);
        
        //stage env
        char *envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
    
        execve("/home/input2/input", argv, envp);
    }
    sleep(2);
    int sockfd;
    struct sockaddr_in server;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("Socket build error!");
        exit(1);
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port = htons(55555);
    if(connect(sockfd, (struct sockaddr*)&server, sizeof(server)) < 0){
        perror("Connect error!");
        exit(1);
    }
    char buf[4] = "\xde\xad\xbe\xef";
    write(sockfd, buf, 4);
    close(sockfd);
    
    return 0;
}

是為了讓解題者滿足程式碼中所需要滿足的條件,總共5個,分別包括:引數傳遞、標準輸入輸出、環境變數、檔案讀寫以及網路通訊方面。

1.argv

  引數第'A''B'位分別為"\x00"和"\x20\x0a\x0d",也就是第65位和第66位(第0位為可執行檔案的路徑),但是'\x00'會截斷。
  於是使用execve執行input檔案,execve函式在unistd(unix standard)標頭檔案中:

int execve(const char path, char const argv[], char *const envp[]);

以argv引數進行傳遞相應引數。

2.stdio

ssize_t read(int fildes, void *buf, size_t nbytes);

摘自 http://codewiki.wikidot.com/c:system-calls:read

Field Description
int fildes The file descriptor of where to read the input. You can either use a file descriptor obtained from the open system call, or you can use 0, 1, or 2, to refer to standard input, standard output, or standard error, respectively.
const void *buf A character array where the read content will be stored.
size_t nbytes The number of bytes to read before truncating the data. If the data to be read is smaller than nbytes, all data is saved in the buffer.
return value Returns the number of bytes that were read. If value is negative, then the system call returned an error.

  可以看到分別需要從stdinstderr讀取相關的資料,但是stderr沒法寫,於是需要用到c中的叫做管道(pipe)的東西可用於子程序與父程序之間的通訊使用;於是子程序向緩衝區寫資料,而父程序先將定義的相應緩衝區分別替換stdin和stderr,之後則可以從緩衝區進行讀取。

3.env

  getenv函式獲取系統中環境變數,這個同樣以execve進行處理,其中的envp引數進行傳遞。

4.file

  常規操作,自己建立一個檔案,然後寫"\x00\x00\x00\x00"進去然後再讀即可。

5.network

  是以傳遞的第C個引數作為監聽埠,以及socket通訊獲取傳來的訊息,採用本地通訊。socket網路程式設計網上一搜就出來的,其實百度百科說的還挺清楚的...中間需要sleep幾秒等待接收資訊的服務開啟,然後傳遞資訊。


程式碼整理:

#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "arpa/inet.h"

int main(){

    //stage argv
    char *argv[101] = {"/home/input2/input", [1 ... 99] = "A", NULL};
    argv['A'] = "\x00";
    argv['B'] = "\x20\x0a\x0d";
    argv['C'] = "55555";

    //stage stdio
    int pipe2stdin[2] = {-1, -1};
    int pipe2stderr[2] = {-1, -1};
    pid_t childpid;

    //stage file
    FILE* fp = fopen("\x0a", "w");
    fwrite("\x00\x00\x00\x00", 4, 1, fp);
    fclose(fp);

    if(pipe(pipe2stdin) < 0 || pipe(pipe2stderr) < 0)
    {
        perror("Cannot create the pipe!");
        exit(1);
    }
    if((childpid = fork()) < 0)
    {
        perror("Cannot fork!");
        exit(1);
    }
    if(childpid == 0) //child process 
    {
        close(pipe2stdin[0]);  //close pipes of read
        close(pipe2stderr[0]);
        
        write(pipe2stdin[1], "\x00\x0a\x00\xff", 4);
        write(pipe2stderr[1], "\x00\x0a\x02\xff", 4);
    }
    else{            //parent process
        close(pipe2stdin[1]); close(pipe2stderr[1]);   //close pipes of write
        dup2(pipe2stdin[0], 0); dup2(pipe2stderr[0], 2);    //change stdin and stderr 
        close(pipe2stdin[0]); close(pipe2stderr[0]);
        
        //stage env
        char *envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
    
        execve("/home/input2/input", argv, envp);
    }
    sleep(2);
    int sockfd;
    struct sockaddr_in server;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("Socket build error!");
        exit(1);
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port = htons(55555);
    if(connect(sockfd, (struct sockaddr*)&server, sizeof(server)) < 0){
        perror("Connect error!");
        exit(1);
    }
    char buf[4] = "\xde\xad\xbe\xef";
    write(sockfd, buf, 4);
    close(sockfd);
    
    return 0;
}

參考連結:https://werewblog.wordpress.com/2016/01/11/pwnable-kr-input/