博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【pwnable.kr】input
阅读量:4206 次
发布时间:2019-05-26

本文共 7189 字,大约阅读时间需要 23 分钟。

下载程序(注意要用bash,zsh报错zsh: no matches found: input2@pwnable.kr:/home/input2/*)

scp -P 2222 -p  input2@pwnable.kr:/home/input2/* ./

昨晚阅读了一下pwnable.kr的官网,里面提到"the main purpose of pwnable.kr is 'fun'.    please consider each of the challenges as a game",感觉挺有意思的。ok,记录一下本关的通关过程。

本关一共有五个小关卡,分为5个stage。

第一关

本关对argc、argv[65]、argv[66]做了判断,本来考虑在bash使用python传递参数给程序,但是\x00实在不知道如何传递,参考网上WP编写C程序,调用execve时传递argv给子程序。

v16 = argv;  v24 = __readfsqword(0x28u);  puts("Welcome to pwnable.kr");  puts("Let's see if you know how to give input to program");  puts("Just give me correct inputs then you will get the flag :)");  if ( argc != 100 )    return 0;  if ( *argv[65] )    return 0;  v4 = __CFADD__(argv, 528LL);  v5 = argv + 66 == 0LL;  v6 = 4LL;  v7 = argv[66];  v8 = " \n\r";  do  {    if ( !v6 )      break;    v4 = (const unsigned __int8)*v7 < *v8;    v5 = *v7++ == *v8++;    --v6;  }  while ( v5 );  if ( (!v4 && !v5) != v4 )    return 0;  puts("Stage 1 clear!");
argc是arguments count,程序运行时参数的数量。没有参数时argc为1。

argv是argument value,传递给程序的argv长度为argc,长度为argc。argv[0]是程序本身,注意argv[argc]需为NULL。

在中也有说明:

#include 
void main(int argc, char **argv, char **env){ char *arg[101]={"/tmp/input2",[1 ... 99]="A", NULL}; arg['A'] = "\x00"; arg['B'] = "\x20\x0a\x0d"; execve(arg[0],arg,NULL);}

第二关

程序从0 stdin和2 stderr读取buf并和字符串比较,因此需要想办法重定向子进程的stdin和stderr。dup2函数可以做这件事情。

重定向了stdin和stderr之后,父进程向子进程写入对于数据即可。

read(0, &buf, 4uLL);  if ( memcmp(&buf, &unk_400E3D, 4uLL) ) // "\x00\x0a\x00\xff"    return 0;  read(2, &buf, 4uLL);  if ( memcmp(&buf, &unk_400E42, 4uLL) ) // "\x00\x0a\x02\xff"    return 0;  puts("Stage 2 clear!");

linux c如何实现进程通信呢?这里有一份很好的资料可以参考。

通过该文章我们可以知道,进程有自己独立的地址空间,需要通过kernel来交换数据。
pipe函数可以在kernel中建立一个半双工的管道用于单向传递数据。一种利用pipe来传递数据的思路如下所示:
1. 进程调用pipe创建自身的读写管道
2. 调用fork生成子进程并close父进程的写端和子进程的读端建立进程间半双工管道
3. 开始传递数据(参考上文链接中的图片)
知道了如何在进程之间传递数据之后,只需要重定向子进程的描述符即可。
dup2函数可以复制描述符,如下程序所示。

#include 
#include
void main(){ dup2(1,3); write(3,"123",3);//程序输出123}

因此,stage2的exp为:

#include 
#include
#include
void main(int argc, char **argv, char **env){ char *arg[101]={"/tmp/input2",[1 ... 99]="A", NULL}; arg['A'] = "\x00"; arg['B'] = "\x20\x0a\x0d"; pid_t pid; int pipefd1[2], pipefd2[2]; //int n; //char buf[100]={0}; if(pipe(pipefd1)<0 || pipe(pipefd2)<0){ exit(-1); } pid = fork(); if(pid == -1){ printf("fork error"); return; }else if(pid >0){ close(pipefd1[0]); close(pipefd2[0]); write(pipefd1[1],"\x00\x0a\x00\xff",4); write(pipefd2[1],"\x00\x0a\x02\xff",4); // wait(NULL); }else if(pid==0){ close(pipefd1[1]); close(pipefd2[1]); dup2(pipefd1[0],0); //pipefd1[0] and 0 dup2(pipefd2[0],2); //pipefd2[0] and 2 //n = read(0,buf,13); //write(1, buf, n); execve(arg[0],arg,NULL); }}

第三关

本关获取环境变量并调用strcmp(IDA这种伪代码看多了就感觉是这函数了)来比较,因此传递环境变量给子程序即可。

v11 = getenv(&s2);                            // \xDE\xAD\xBE\xEF  v12 = 5LL;  v13 = &unk_400E5B;                            // \xCA\xFE\xBA\xBE  v14 = v11;  do  {    if ( !v12 )      break;    v9 = *v13 < (unsigned __int8)*v14;    v10 = *v13++ == *v14++;    --v12;  }  while ( v10 );  if ( (!v9 && !v10) != v9 )    return 0;  puts("Stage 3 clear!");
       
envp is an array of strings, conventionally of the form
key=value, which are
       passed as environment to the new program.  The argv and envp arrays
       must each include a null pointer at the end of the array.
#include 
#include
#include
void main(){... char *env[2] = {"\xDE\xAD\xBE\xEF=\xCA\xFE\xBA\xBE", NULL}; ... execve(argv[0],argv, env); }}

第四关

本关调用fopen打开文件,读取4byte。

stream = fopen("\n", "r");  if ( !stream )    return 0;  if ( fread(&buf, 4uLL, 1uLL, stream) != 1 )    return 0;  if ( memcmp(&buf, &unk_400E73, 4uLL) )        // \x00\x00\x00\x00    return 0;  fclose(stream);  puts("Stage 4 clear!");
因此调用fopen打开文件,fwrite写入相应数据最后fclose即可。(如果是在windows给linux的共享目录下操作需要拷贝到tmp下运行程序)
//stage 4    FILE *fp = fopen("\x0a", "w+");	if (fp == NULL) {		perror("Open file recfile");		exit(-1);	}    char *data = "\x00\x00\x00\x00";    fwrite(data, sizeof(char), 4, fp);    fclose(fp);第五关

第五关
这关IDA伪代码可以看,不过不太友好,直接看源码吧。程序从argv['C']获取端口绑定并接受4byte,比较成功后get flag。

// network	int sd, cd;	struct sockaddr_in saddr, caddr;	sd = socket(AF_INET, SOCK_STREAM, 0);	if(sd == -1){		printf("socket error, tell admin\n");		return 0;	}	saddr.sin_family = AF_INET;	saddr.sin_addr.s_addr = INADDR_ANY;	saddr.sin_port = htons( atoi(argv['C']) );	if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){		printf("bind error, use another port\n");    		return 1;	}	listen(sd, 1);	int c = sizeof(struct sockaddr_in);	cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);	if(cd < 0){		printf("accept error, tell admin\n");		return 0;	}	if( recv(cd, buf, 4, 0) != 4 ) return 0;	if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;	printf("Stage 5 clear!\n");	// here's your flag	system("/bin/cat flag");

socket编程相关的函数说明参考这里吧,没什么想说的。https://akaedu.github.io/book/ch37s02.html

// stage 5      argv['C'] = "7777";    ...    sleep(1);    struct sockaddr_in servaddr;    int sockfd;    char *str = "\xde\xad\xbe\xef";    sockfd = socket(AF_INET, SOCK_STREAM, 0);    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);    servaddr.sin_port = htons(atoi(argv['C']));    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));    write(sockfd, str, strlen(str));    close(sockfd);

EXP

#include 
#include
#include
#include
#include
#include
#include
void main(){ char *argv[101]={"/home/input2/input",[1 ... 99]="A", NULL}; argv['A'] = "\x00"; argv['B'] = "\x20\x0a\x0d"; argv['C'] = "7777"; //stage 3 char *env[2] = {"\xDE\xAD\xBE\xEF=\xCA\xFE\xBA\xBE", NULL}; //stage 4 FILE *fp = fopen("\x0a", "wb"); if (fp == NULL) { perror("Open file recfile"); exit(-1); } char *data = "\x00\x00\x00\x00"; fwrite(data, sizeof(char), 4, fp); fclose(fp); //stage2 pid_t pid; int pipefd1[2], pipefd2[2]; //int n; //char buf[100]={0}; if(pipe(pipefd1)<0 || pipe(pipefd2)<0){ exit(-1); } pid = fork(); if(pid == -1){ printf("fork error"); return; }else if(pid >0){ close(pipefd1[0]); close(pipefd2[0]); write(pipefd1[1],"\x00\x0a\x00\xff",4); write(pipefd2[1],"\x00\x0a\x02\xff",4); // wait(NULL); // stage 5 sleep(1); struct sockaddr_in servaddr; int sockfd; char *str = "\xde\xad\xbe\xef"; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); servaddr.sin_port = htons(atoi(argv['C'])); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); write(sockfd, str, strlen(str)); close(sockfd); }else if(pid==0){ close(pipefd1[1]); close(pipefd2[1]); dup2(pipefd1[0],0); //pipefd1[0] and 0 dup2(pipefd2[0],2); //pipefd2[0] and 2 //n = read(0,buf,13); //write(1, buf, n); execve(argv[0],argv, env); }}
最后修改程序路径,上传程序运行即可。
scp -P 2222 -p  exp input2@pwnable.kr:/tmp/exp/
恭喜通关。
你可能感兴趣的文章
PrefTest工作室全新力作-《性能测试与调优实战》课程视频即将上线
查看>>
质量度量分析与测试技术 培训大纲
查看>>
欢迎加入【亿能测试快讯】邮件列表!
查看>>
为什么我们的自动化测试“要”这么难
查看>>
LoadRunner性能脚本开发实战训练
查看>>
测试之途,前途?钱途?图何?
查看>>
测试设计与测试项目实战训练
查看>>
HP Sprinter:敏捷加速器
查看>>
单元测试培训PPT
查看>>
adb常用命令
查看>>
通过LR监控Linux服务器性能
查看>>
通过FTP服务的winsockes录制脚本
查看>>
LRwinsocket协议测试AAA服务器
查看>>
Net远程管理实验
查看>>
反病毒专家谈虚拟机技术 面临两大技术难题
查看>>
几种典型的反病毒技术:特征码技术、覆盖法技术等
查看>>
性能测试一般过程与LR性能测试过程
查看>>
Software Security Testing软件安全测试
查看>>
SQL注入漏洞全接触--进阶篇
查看>>
SQL注入漏洞全接触--高级篇
查看>>