博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AliCTF 2015-题目解析之代码血案
阅读量:5310 次
发布时间:2019-06-14

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

一、题目说明:

  1. 服务器端有个程序在监听端口30000,该程序有一个安全漏洞,通过阅读源码找出漏洞。
  2. 向该程序提交请求,如果触发漏洞,便会在响应中发回88字节的flag
  3. 每个参赛者有自己的Token,提交的请求中需要包含Token,每个Token只有四次提交请求的机会

二、解题工具:

BurpSuit + WireShark

工具说明:

用自己的python代理,给服务器发送数据,服务器不响应,用浏览器却可以,使用wireshark抓包比较,也没发现区别(希望有大神告诉我为什么),为了节省时间干脆就用BurpSuit Repeater发包,用WireShark查看收到的数据包内容。。。

三、解题过程:

1、理解源码

程序的流程

一系列初始化 -> 监听端口 -> 接受请求 -> 判断格式 -> 判断token -> 交给rpc函数处理

rpc函数列表

  1. 0 rpc_strcat
  2. 1 rpc_insert
  3. 2 rpc_setstr
  4. 3 rpc_replace
  5. 4 rpc_delete
  6. 5 rpc_strcmp
  7. 6 rpc_readlog
  8. 7 rpc_test

2、发现漏洞

看到rpc_readlog、writelog,凭直觉这两个函数有蹊跷(没事提供读写日志功能干嘛- -!),虽然察觉到了,但是发现漏洞的过程还是很艰难(- -!)。

经过漫长曲折的分析,最后发现问题确实出现在rpc_readlog中,rpc_readlog函数的代码如下:

1 response_t *rpc_readlog(request_t *request){ 2     req_t req; 3     memcpy(&req,request->data,sizeof(req_t)); 4     if(req.str1.len != strlen(req.str1.string) || req.str2.len != strlen(req.str2.string) || req.str1.len != 0 || req.str2.len != 0 || req.pos < 0 || req.len == 0 || validkey(&req) == FAIL){ 5         char *message = "Wrong Argument."; 6         return create_response(strlen(message), message); 7     } 8      9     char *buffer;10     char len = req.len;11     12     if(len < 0)13         len = -len;14     if((buffer = (char*)malloc(len + SAFE_SPACE_LEN)) == NULL){15         char *message = "Malloc Error.";16         return create_response(strlen(message),message);17     }18     memset(buffer,0,len + SAFE_SPACE_LEN);19     if((readlog(request->token,request->len_token,&req,buffer)) < 0){20         free(buffer);21         char *message = "Operation Error.";22         return create_response(strlen(message),message);23     }24     25     response_t *response = create_response(len,buffer);26     free(buffer);27     return response;28 }

 

代码中使用有符号char型变量len存储长度,如果len小于0就取负得到正的len,然后为buffer分配空间,长度为len+ 130(SAFE_SPACE_LEN),问题就出在这里,为什么呢?

在本地gcc编译环境中进行试验,发现当len 等于 -128(0x80) 时,负 len 还等于-128(0x80) ,而-128又是合法的值。
如果直接将len传给malloc,分配空间是会失败的,但是,代码中给len加上了个SAFE_SPACE_LEN,这会导致成功给buffer分配2字节的空间,最终造成了后面的”血案“。
rpc_readlog为buffer分配空间后,调用了readlog函数:

1 int readlog(char* token,unsigned char len_token,req_t* req,char* response){ 2     int fd; 3     int pos = req->pos; 4     char len = req->len; 5     char filename[512] = "/rpcserver/"; 6     char *filecontent; 7     int filesize = 0; 8     struct stat buf; 9 10     if(pos < 0)11         return FAIL;12     if(len < 0){13         pos = pos + len;14         len = -len;15     }16     assert(SAFE_POSITIVE(len));17 18     memcpy(filename + 11,token,len_token);19     memcpy(filename + 11 + len_token,".log",5);20 21     if((fd = open(filename,O_RDONLY,0644)) < 0)22         return FAIL;23     if(fstat(fd,&buf) < 0){24         close(fd);25         return FAIL;26     }27     filesize = buf.st_size;28     if(pos < 0 || pos > filesize || pos + len > filesize || (filecontent = (char*)malloc(filesize)) == NULL ){29         close(fd);30         return FAIL;31     }32 33     if(filesize != read(fd,filecontent,filesize)){34         close(fd);35         free(filecontent);36         return FAIL;37     }38     39     char l = len;40     do{41         response[l] = filecontent[pos + l];42         l--;43     }while(l >= 0);44     45     close(fd);46     free(filecontent);47     return SUCCESS;48 }

 

这个函数的功能就是读取log文件中的部分内容,读取的位置和长度由请求中的pos和len指定,最后将读取的内容写到buffer中,我们来分析一下len = -128时,是否可以通过合法性检测并触发漏洞。

首先注意到开始部分有个断言:

  1. assert(SAFE_POSITIVE(len));

这个断言咋一看看挺“吓人”的,但实际SAFE_POSITIVE宏的定义是这样的:

  1. #define SAFE_POSITIVE(x)( x = x >0? x : x + SAFE_SPACE_LEN )>0

也就是说len>0或者len+ SAFE_SPACE_LEN >0就可以了,换句话说,len=-128是没问题的。再看后面的合法性检查,可以总结出当len = -128时,只要pos > 128就可以了,当然还要求存在log文件且有一定长度的内容。

看来len=-128通过合法性检测是没有问题了,现在直接看最后这个循环:

1     char l = len;2     do{3         response[l] = filecontent[pos + l];4         l--;5     }while(l >= 0);

 

filecontent中存储了log文件中的所有内容,这个循环便是将指定pos和len的内容复制到response中,response就是rpc_readlog中分配了2字节长度的buffer。当 l = -128时,l-- = 127,也就是会给response[-128]以及response[0]->response[127]的内存空间赋值,结果就是导致堆溢出。

3、漏洞触发要求

上一节详细说明了漏洞的原理,现在总结一下触发漏洞都有那些要求:

  • ((req_t*)request->data)->len = -128
  • ((req_t*)request->data)->pos > 128
  • log文件存在且有内容:通过执行其他功能生成log文件

根据要求不难看出漏洞触发需要提交两次请求:

  • 请求1:使用rpc_strcat功能生成log文件
  • 请求2:使用rpc_readlog功能触发漏洞

4、分析请求格式

漏洞触发要求已经明确,在发送请求来触发漏洞之前还有一件非常重要的工作:分析请求格式。

每个人只有4次调用rpc功能的机会,本菜由于请求格式问题,前两次都没有成功调用rpc,导致后两次到了“不成功,便成仁”的境地(唉,在点发送请求的时候手抖啊)。

根据源码,可以分析出请求格式如下



其中需要注意两点:

  • 网络字节序和主机字节序的转换,影响字段为 request_t->len_data
  • 内存对齐问题,影响request_t->data向req_t结构的映射

5、漏洞的触发

首先需要调用rpc_strcat功能,构造请求如下


strcat.PNG


rpc_strcat功能执行成功后,服务器会返回拼接的字符串,同时由于设置了logRequest为1,会在服务器上生成相关log文件。

下面是调用rpc_readlog功能时的请求,注意请求中 pos = 0x00000082,len=0x80。


捕获.PNG


请求发送后,服务器返回flag


捕获1.PNG


总结:解题关键知识点

  • 计算机处理有符号整数的规则
  • 网络字节序与主机字节序
  • 结构体的内存对齐
  • 堆溢出

转载于:https://www.cnblogs.com/Wrong-Side/p/4380445.html

你可能感兴趣的文章
将多张图片和文字合成一张图片
查看>>
自己动手写ORM(01):解析表达式树生成Sql碎片
查看>>
如何使用USBWebserver在本机快速建立网站测试环境
查看>>
百度Ueditor编辑器的Html模式自动替换样式的解决方法
查看>>
变量提升
查看>>
线性表可用顺序表或链表存储的优缺点
查看>>
在现有的mysql主从基础上,搭建mycat实现数据的读写分离
查看>>
[Flex] flex手机项目如何限制横竖屏?只允许横屏?
查看>>
tensorflow的graph和session
查看>>
JavaScript动画打开半透明提示层
查看>>
Mybatis生成resulteMap时的注意事项
查看>>
jquery-jqzoom 插件 用例
查看>>
1007. Maximum Subsequence Sum (25)
查看>>
iframe的父子层跨域 用了百度的postMessage()方法
查看>>
图片生成缩略图
查看>>
动态规划 例子与复杂度
查看>>
查看oracle数据库的连接数以及用户
查看>>
【数据结构】栈结构操作示例
查看>>
中建项目环境迁移说明
查看>>
三.野指针和free
查看>>