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. |
可以看到分別需要從stdin
和stderr
讀取相關的資料,但是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/