btwotch    

, Deutschland · männlich · registriert seit 2008 · heute zuletzt online


mailto, against spam!

Ptrace VI. - how to migrate a process into a screen session

Computer · · 1 Kommentar

Question: how to migrate a process into a screen session?
Answer:

  1. move filedescriptors std{in, out, err} to a pts in screen
  2. ignore SIGHUP

In Assembler:

    1 #include <stdio.h>
    2 #include <asm/unistd.h>
    3 
    4 int main()
    5 {
    6   /*
    7      struct sigaction sa;
    8 
    9      sa.sa_handler= SIG_IGN;
   10 
   11      syscall(__NR_rt_sigaction, SIGHUP, &sa, NULL, 8);
   12    */
   13 
   14   __asm__(
   15     "_start:\n"
   16     "subq   $160, %rsp\n"
   17     "movq   $1, -160(%rbp)\n"
   18     "movq   $1, %rdi\n"
   19     "leaq   -160(%rbp), %rsi\n"
   20     "movq   $0, %rdx\n"
   21     "mov    $8, %r10d\n"
   22     "movq   $13, %rax\n"
   23     "syscall\n"
   24     "mov    $0x2, %rax\n" // open
   25     "movq   $0xffffffffffff006c, %rbx\n"
   26     "push   %rbx\n"
   27     "movq   $0x6c756e2f7665642f, %rbx\n"
   28     "push   %rbx\n"
   29     "movq   %rsp, %rdi\n"
   30     "movq   $0x1, %rsi\n" // open WRONLY
   31     "syscall\n"
   32     "movq   %rax, %rdi\n"
   33     "movq   $0x01, %rsi\n"
   34     "movq   $0x21, %rax\n"
   35     "syscall\n"
   36     // now the same for STDIN
   37     "mov    $0x2, %rax\n" // open
   38     "movq   %rsp, %rdi\n"
   39     "movq   $0x0, %rsi\n" // open RDONLY
   40     "syscall\n"
   41     "movq   %rax, %rdi\n"
   42     "movq   $0x00, %rsi\n"
   43     "movq   $0x21, %rax\n"
   44     "syscall\n"
   45     // and now STDERR
   46     "mov    $0x2, %rax\n" // open
   47     "movq   %rsp, %rdi\n"
   48     "movq   $0x1, %rsi\n" // open WRONLY
   49     "syscall\n"
   50     "movq   %rax, %rdi\n"
   51     "movq   $0x02, %rsi\n"
   52     "movq   $0x21, %rax\n"
   53     "syscall\n"
   54 
   55   );
   56 
   57 }
  • 15: that's just a marker for me to be able to find the function later more easy
  • 16: decrease the stack pointer to put a struct sigaction in there
  • 17: put SIG_IGN in there - SIG_IGN is just the number 1
  • 18: first argument for the rt_sigaction syscall is on which signal you want to operate; we want to ignore signal SIGHUP - SIGHUP has the value 1
  • 19: second argument for the rt_sigaction syscall is the pointer to the sigaction struct
  • 20: in case you want to store the old signal behaviour you could set the third argument to non-NULL; we don't do this here and set it to null
  • 21: the fourth argument is size_t sigsetsize; that's 8
  • 22: so, as specified in /usr/include/asm/unistd_64.h the syscall number for rt_sigaction is 13; so we just put that into rax
  • 23: syscall: just execute the syscall
    that's how easy it is to ignore a signal ;)
  • 24: now, we want to open a file; the syscall number for open is 2
  • 25: move "\0l" into rbx
  • 26: push that on the stack
  • 27: move "/dev/nul" into rbx
  • 28: push that on the stack
  • 29: first argument for open is which file you want to open; filename is stored on the stack at rsp
  • 30: we open the file in mode O_WRONLY; thats a symbol for 1
  • 31: execute that syscall
    that's how easy it is to open the file /dev/null in mode O_WRONLY
    I put "/dev/null\0" on the stack, because it would be to complicated to put it into the data segment and adjust the address correctly.
  • 32: the open syscall returned in eax the filedescriptor number; we want to use that number as second argument for dup2; so we put it into rdi
  • 33: first argument of dup2 is which filedescriptor should be moved; that's stdout (number 1)
  • 34: syscall number for dup2 is 33 or 0x21
  • 35: execute the syscall
    that's how easy it is to move the stdout to /dev/null
  • 37: 0x2 is the syscall for the open syscall
  • 38: we want to open the file "/dev/null\0"
  • 39: second argument for open is how we want to open that file; we want to open it O_RDONLY (=0)
  • 40: just syscall
    now we have opened "/dev/null\0" twice; first WRONLY, second RDONLY
  • 41: same as in line 32
  • 42: we want to move stdin (=0)
  • 43: same as in line 34
  • 44: syscall
    now we have connected stdin to RDONLY opened /dev/null
    lines 45 and following do the same for stderr and WRONLY /dev/null

in binary-code:

    1 #define PRE_CMDS        "\x48\x81\xec\xa0\x00\x00\x00" \
    2                         "\x48\xc7\x85\x60\xff\xff\xff\x01\x00\x00\x00" \
    3                         "\x48\xc7\xc7\x01\x00\x00\x00" \
    4                         "\x48\x8d\xb5\x60\xff\xff\xff" \
    5                         "\x48\xc7\xc2\x00\x00\x00\x00" \
    6                         "\x41\xba\x08\x00\x00\x00" \
    7                         "\x48\xc7\xc0\x0d\x00\x00\x00" \
    8                         "\x0f\x05" \
    9                         "\x48\xc7\xc0\x02\x00\x00\x00"
   10 #define POST_CMDS       "\x48\x89\xe7\x48\xc7\xc6" \
   11                         "\x01\x00\x00\x00\x0f\x05\x48\x89" \
   12                         "\xc7\x48\xc7\xc6\x01\x00\x00\x00" \
   13                         "\x48\xc7\xc0\x21\x00\x00\x00\x0f" \
   14                         "\x05\x48\xc7\xc0\x02\x00\x00\x00" \
   15                         "\x48\x89\xe7\x48\xc7\xc6\x00\x00" \
   16                         "\x00\x00\x0f\x05\x48\x89\xc7\x48" \
   17                         "\xc7\xc6\x00\x00\x00\x00\x48\xc7" \
   18                         "\xc0\x21\x00\x00\x00\x0f\x05\x48" \
   19                         "\xc7\xc0\x02\x00\x00\x00\x48\x89" \
   20                         "\xe7\x48\xc7\xc6\x01\x00\x00\x00" \
   21                         "\x0f\x05\x48\x89\xc7\x48\xc7\xc6" \
   22                         "\x02\x00\x00\x00\x48\xc7\xc0\x21" \
   23                         "\x00\x00\x00\x0f\x05"

the pts's filename (e.g. /dev/pts/5) must be pushed between PRE_CMDS and POST_CMDS on the stack
the function that is glueing that all together:

    1 #define PRE_CMD "\x48\xbb"
    2 #define POST_CMD "\x53"
    3 
    4 void create_shellcode(char *ttyfile, char *code)
    5 {
    6   char str[8];
    7   int i, j = 0;
    8   int cp = 0; // codepointer
    9   int len;
   10 
   11   len = create_shellcode_len(ttyfile);
   12 
   13   memcpy(code+cp, PRE_CMDS, PRE_CMDS_LEN);
   14   cp+= PRE_CMDS_LEN;
   15   memset(str, 0xff, 8);
   16   for (i = strlen(ttyfile); i >= 0; i--)
   17     {
   18       str[i%8] = ttyfile[i];
   19       if (i%8 == 0)
   20         {
   21           memcpy(code+cp, PRE_CMD, PRE_CMD_LEN);
   22           cp+= PRE_CMD_LEN;
   23 
   24           for (j = 0; j < 8; j++)
   25             {
   26               code[cp] = str[j];
   27               cp++;
   28             }
   29 
   30           memcpy(code+cp, POST_CMD, POST_CMD_LEN);
   31           cp+= POST_CMD_LEN;
   32         }
   33 
   34     }
   35   memcpy(code+cp, POST_CMDS, POST_CMDS_LEN);
   36   cp+= POST_CMDS_LEN;
   37 
   38 
   39   if (cp != len)
   40     {
   41       printf("cp: %d len: %d\n", cp, len);
   42       printf("tut tut tu\n");
   43     }
   44 
   45 }

  • '\x48\xbb' is: mov XXX, %rbx
  • '\x53\ is: push %rbx

now inject the code like http://www.spin.de/hp/btwotch/blog/id/10236046

    1 int main(int argc, char **argv)
    2 {
    3   process = atoi(argv[1]);
    4   struct user_regs_struct regs;
    5   char tty[100];
    6   int len;
    7   struct sigaction sa;
    8 
    9   //get the ttyname
   10   len = readlink("/proc/self/fd/0", tty, 100);
   11   if (len > 99)
   12     exit(-1);
   13   else
   14     tty[len] = '\0';
   15 
   16   len = create_shellcode_len(tty);
   17   char *insertcode = malloc(len);
   18   char backup[len];
   19   create_shellcode(tty, insertcode);
   20 
   21 #ifdef INTERACTIVE
   22   int i;
   23   for (i = 0; i < len; i++)
   24     printf("0x%.hhx ", insertcode[i]);
   25   printf("\n");
   26 #endif
   27 
   28   process = atoi(argv[1]);
   29 
   30   ptrace(PTRACE_ATTACH, process, NULL, NULL);
   31   perror("Attached");
   32   waitinput();
   33   ptrace(PTRACE_GETREGS, process, NULL, &regs);
   34   perror("Got regs");
   35   waitinput();
   36   getdata(process, regs.rip, backup, len);
   37   perror("Got backup (@rip)");
   38   waitinput();
   39   putdata(process, regs.rip, insertcode, len);
   40   perror("Put code");
   41   waitinput();
   42   ptrace(PTRACE_CONT, process, NULL, NULL);
   43   perror("Cont");
   44   waitinput();
   45   wait(NULL);
   46   perror("Wait");
   47   waitinput();
   48   putdata(process, regs.rip, backup, len);
   49   ptrace(PTRACE_SETREGS, process, NULL, &regs);
   50   perror("Put regs");
   51   waitinput();
   52 
   53   ptrace(PTRACE_DETACH, process, NULL, NULL);
   54 
   55   sa.sa_handler = termination_handler;
   56   sigaction(SIGINT, &sa, NULL);
   57 
   58 
   59   setvbuf(stdout,(char*)NULL,_IOLBF,8);
   60 
   61   for (;;)
   62     sleep(255);
   63 
   64 
   65   return 0;
   66 }
  • 10: read which tty/pts the process, you want to move, is using
  • 19: generate shellcode
  • 46: inject code, wait until executed
  • 49: revert origin code
  • 50: revert origin registers
  • 57: termination-handler for SIGINT (e.g. ctrl-c)
  • 60: switch on linebuffer

TODO:

  • ctrl-z should be made possible (I heard you would need a kernel patch for that)
  • revert old sighup-handler after having moved process
  • detect if process is using stdout/stdin/stderr and rewrite correctly -  ./a.out > outputfile
  • other architectures, IA-32, ...
  • fix getppid()


now the complete source code:

    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <string.h>
    4 #include <signal.h>
    5 #include <sys/ptrace.h>
    6 #include <asm/ptrace-abi.h>
    7 #include <sys/reg.h>
    8 #include <sys/user.h>
    9 #include <unistd.h>
   10 #include <bits/wordsize.h>
   11 #include <sys/types.h>
   12 #include <sys/wait.h>
   13 
   14 
   15 #define PRE_CMD_LEN 2
   16 #define PRE_CMD "\x48\xbb"
   17 #define POST_CMD "\x53"
   18 #define POST_CMD_LEN 1
   19 
   20 #define PRE_CMDS_LEN 61
   21 #define PRE_CMDS    "\x48\x81\xec\xa0\x00\x00\x00" \
   22          "\x48\xc7\x85\x60\xff\xff\xff\x01\x00\x00\x00" \
   23          "\x48\xc7\xc7\x01\x00\x00\x00" \
   24          "\x48\x8d\xb5\x60\xff\xff\xff" \
   25          "\x48\xc7\xc2\x00\x00\x00\x00" \
   26          "\x41\xba\x08\x00\x00\x00" \
   27          "\x48\xc7\xc0\x0d\x00\x00\x00" \
   28          "\x0f\x05" \
   29          "\x48\xc7\xc0\x02\x00\x00\x00"
   30 #define POST_CMDS    "\x48\x89\xe7\x48\xc7\xc6" \
   31          "\x01\x00\x00\x00\x0f\x05\x48\x89" \
   32          "\xc7\x48\xc7\xc6\x01\x00\x00\x00" \
   33          "\x48\xc7\xc0\x21\x00\x00\x00\x0f" \
   34          "\x05\x48\xc7\xc0\x02\x00\x00\x00" \
   35          "\x48\x89\xe7\x48\xc7\xc6\x00\x00" \
   36          "\x00\x00\x0f\x05\x48\x89\xc7\x48" \
   37          "\xc7\xc6\x00\x00\x00\x00\x48\xc7" \
   38          "\xc0\x21\x00\x00\x00\x0f\x05\x48" \
   39          "\xc7\xc0\x02\x00\x00\x00\x48\x89" \
   40          "\xe7\x48\xc7\xc6\x01\x00\x00\x00" \
   41          "\x0f\x05\x48\x89\xc7\x48\xc7\xc6" \
   42          "\x02\x00\x00\x00\x48\xc7\xc0\x21" \
   43          "\x00\x00\x00\x0f\x05"
   44 #define POST_CMDS_LEN 107
   45 
   46 
   47 //#define INTERACTIVE
   48 void waitinput()
   49 {
   50 #ifdef INTERACTIVE
   51   getchar();
   52 #endif
   53 }
   54 
   55 pid_t process;
   56 
   57 void termination_handler(int signum)
   58 {
   59   kill(process, SIGINT);
   60   exit(0);
   61 }
   62 
   63 int create_shellcode_len(char *ttyfile)
   64 {
   65   int i, j;
   66 
   67   j = (strlen(ttyfile)/8)+1;
   68   i = (j*8)*1;
   69   i += POST_CMDS_LEN+PRE_CMDS_LEN;
   70   i += (PRE_CMD_LEN+POST_CMD_LEN)*j;
   71   return i;
   72 }
   73 
   74 void create_shellcode(char *ttyfile, char *code)
   75 {
   76   char str[8];
   77   int i, j = 0;
   78   int cp = 0; // codepointer
   79   int len;
   80 
   81   len = create_shellcode_len(ttyfile);
   82 
   83   memcpy(code+cp, PRE_CMDS, PRE_CMDS_LEN);
   84   cp+= PRE_CMDS_LEN;
   85   memset(str, 0xff, 8);
   86   for (i = strlen(ttyfile); i >= 0; i--)
   87     {
   88       str[i%8] = ttyfile[i];
   89       if (i%8 == 0)
   90         {
   91           memcpy(code+cp, PRE_CMD, PRE_CMD_LEN);
   92           cp+= PRE_CMD_LEN;
   93 
   94           for (j = 0; j < 8; j++)
   95             {
   96               code[cp] = str[j];
   97               cp++;
   98             }
   99 
  100           memcpy(code+cp, POST_CMD, POST_CMD_LEN);
  101           cp+= POST_CMD_LEN;
  102         }
  103 
  104     }
  105   memcpy(code+cp, POST_CMDS, POST_CMDS_LEN);
  106   cp+= POST_CMDS_LEN;
  107 
  108 
  109   if (cp != len)
  110     {
  111       printf("cp: %d len: %d\n", cp, len);
  112       printf("tut tut tu\n");
  113     }
  114 
  115 }
  116 
  117 void getdata(pid_t child, long addr, char *str, int len)
  118 {
  119   char *laddr;
  120   int i = 0,j;
  121   union u
  122   {
  123     long val;
  124     char chars[sizeof(long)];
  125   } data;
  126 
  127   j = len / sizeof(long);
  128   laddr = str;
  129 
  130   while (i < j)
  131     {
  132       data.val = ptrace(PTRACE_PEEKDATA, child, addr+(i * sizeof(long)),
  133                  NULL);
  134       memcpy(laddr, data.chars, sizeof(long));
  135       i++;
  136       laddr += sizeof(long);
  137     }
  138   j = len % sizeof(long);
  139 
  140   if (j != 0)
  141     {
  142       data.val = ptrace(PTRACE_PEEKDATA, child, addr+(i * sizeof(long)),
  143                  NULL);
  144       memcpy(laddr, data.chars, j);
  145     }
  146 
  147   str[len] = '\0';
  148 
  149 
  150 }
  151 
  152 void putdata(pid_t child, long addr, char *str, int len)
  153 {
  154   char *laddr;
  155   int i = 0, j;
  156   union u
  157   {
  158     long val;
  159     char chars[sizeof(long)];
  160   } data;
  161 
  162   j = len/sizeof(long);
  163   laddr = str;
  164 
  165   while (i < j)
  166     {
  167       memcpy(data.chars, laddr, sizeof(long));
  168       ptrace(PTRACE_POKEDATA, child, addr+(i * sizeof(long)), data.val);
  169       i++;
  170       laddr += sizeof(long);
  171     }
  172   j = len % sizeof(long);
  173 
  174   if (j != 0)
  175     {
  176       memcpy(data.chars, laddr, j);
  177       ptrace(PTRACE_POKEDATA, child, addr+(i * sizeof(long)), data.val);
  178     }
  179 
  180 }
  181 
  182 
  183 int main(int argc, char **argv)
  184 {
  185   process = atoi(argv[1]);
  186   struct user_regs_struct regs;
  187   char tty[100];
  188   int len;
  189   struct sigaction sa;
  190 
  191   //get the ttyname
  192   len = readlink("/proc/self/fd/0", tty, 100);
  193   if (len > 99)
  194     exit(-1);
  195   else
  196     tty[len] = '\0';
  197 
  198   len = create_shellcode_len(tty);
  199   char *insertcode = malloc(len);
  200   char backup[len];
  201   create_shellcode(tty, insertcode);
  202 
  203 #ifdef INTERACTIVE
  204   int i;
  205   for (i = 0; i < len; i++)
  206     printf("0x%.hhx ", insertcode[i]);
  207   printf("\n");
  208 #endif
  209 
  210   process = atoi(argv[1]);
  211 
  212   ptrace(PTRACE_ATTACH, process, NULL, NULL);
  213   perror("Attached");
  214   waitinput();
  215   ptrace(PTRACE_GETREGS, process, NULL, &regs);
  216   perror("Got regs");
  217   waitinput();
  218   getdata(process, regs.rip, backup, len);
  219   perror("Got backup (@rip)");
  220   waitinput();
  221   putdata(process, regs.rip, insertcode, len);
  222   perror("Put code");
  223   waitinput();
  224   ptrace(PTRACE_CONT, process, NULL, NULL);
  225   perror("Cont");
  226   waitinput();
  227   wait(NULL);
  228   perror("Wait");
  229   waitinput();
  230   putdata(process, regs.rip, backup, len);
  231   ptrace(PTRACE_SETREGS, process, NULL, &regs);
  232   perror("Put regs");
  233   waitinput();
  234 
  235   ptrace(PTRACE_DETACH, process, NULL, NULL);
  236 
  237   sa.sa_handler = termination_handler;
  238   sigaction(SIGINT, &sa, NULL);
  239 
  240 
  241   setvbuf(stdout,(char*)NULL,_IOLBF,8);
  242 
  243   for (;;)
  244     sleep(255);
  245 
  246 
  247   return 0;
  248 }

See also:

23. Januar 2011 23:33

Tags:  ·  ·  ·  ·  ·  ·  ·  ·

25. März 2010

Sonstiges · · 1 Kommentar

Nom nom nom. Danke wastl für den Tipp.

25. März 2010 05:17

Tags:

Ptrace V.

Computer · · 1 Kommentar

Diesesmal soll Code in ein anderes Programm injected werden und das auf 64bit. Es gibt mehrere Anleitungen fuer 32bit:

Zuerst einmal das Programm in das wir den Code injecten wollen:

    1 #include <stdio.h>
    2 #include <sys/types.h>
    3 #include <sys/stat.h>
    4 #include <fcntl.h>
    5 
    6 
    7 int main()
    8 {
    9   int fd;
   10 
   11   printf("my pid: %d\n", getpid());
   12   getchar();
   13 
   14   printf("blablawindows ist cool\n");
   15   printf("windows ist cool\n");
   16 
   17   fd = 2+2;
   18 
   19   fd = open("/dev/null", O_WRONLY);
   20 
   21   write(fd, "blabla", 6);
   22   close(fd);
   23   getchar();
   24 
   25   exit(13);
   26 }

Und jetzt der Quellcode des Code-Injectors:

    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <string.h>
    4 #include <sys/ptrace.h>
    5 #include <asm/ptrace-abi.h>
    6 #include <sys/reg.h>
    7 #include <sys/user.h>
    8 #include <unistd.h>
    9 #include <bits/wordsize.h>
   10 #include <sys/types.h>
   11 #include <sys/wait.h>
   12 
   13 void getdata(pid_t child, long addr, char *str, int len)
   14 {
   15   char *laddr;
   16   int i = 0,j;
   17   union u
   18   {
   19     long val;
   20     char chars[sizeof(long)];
   21   } data;
   22 
   23   j = len / sizeof(long);
   24   laddr = str;
   25 
   26   while (i < j)
   27     {
   28       data.val = ptrace(PTRACE_PEEKDATA, child, addr+(i * sizeof(long)),
   29                  NULL);
   30       memcpy(laddr, data.chars, sizeof(long));
   31       i++;
   32       laddr += sizeof(long);
   33     }
   34   j = len % sizeof(long);
   35 
   36   if (j != 0)
   37     {
   38       data.val = ptrace(PTRACE_PEEKDATA, child, addr+(i * sizeof(long)),
   39                  NULL);
   40       memcpy(laddr, data.chars, j);
   41     }
   42 
   43   str[len] = '\0';
   44 
   45 
   46 }
   47 
   48 void putdata(pid_t child, long addr, char *str, int len)
   49 {
   50   char *laddr;
   51   int i = 0, j;
   52   union u
   53   {
   54     long val;
   55     char chars[sizeof(long)];
   56   } data;
   57 
   58   j = len/sizeof(long);
   59   laddr = str;
   60 
   61   while (i < j)
   62     {
   63       memcpy(data.chars, laddr, sizeof(long));
   64       ptrace(PTRACE_POKEDATA, child, addr+(i * sizeof(long)), data.val);
   65       i++;
   66       laddr += sizeof(long);
   67     }
   68   j = len % sizeof(long);
   69 
   70   if (j != 0)
   71     {
   72       memcpy(data.chars, laddr, j);
   73       ptrace(PTRACE_POKEDATA, child, addr+(i * sizeof(long)), data.val);
   74     }
   75 
   76 }
   77 
   78 
   79 int main(int argc, char **argv)
   80 {
   81   pid_t process;
   82   struct user_regs_struct regs;
   83 
   84   char insertcode[] =
   85     "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53"
   86     "\x54\x5f\x52\x57\x54\x5e\x0f\x05";
   87 
   88   int len = 25;
   89   char backup[len];
   90 
   91 
   92   process = atoi(argv[1]);
   93 
   94   ptrace(PTRACE_ATTACH, process, NULL, NULL);
   95   perror("Attached");
   96   getchar();
   97   ptrace(PTRACE_GETREGS, process, NULL, &regs);
   98   perror("Got regs");
   99   getchar();
  100   putdata(process, regs.rip, insertcode, len);
  101   perror("Put code");
  102   getchar();
  103   ptrace(PTRACE_CONT, process, NULL, NULL);
  104   perror("Cont");
  105   getchar();
  106 
  107 
  108   ptrace(PTRACE_DETACH, process, NULL, NULL);
  109 
  110 
  111   return 0;
  112 }

  • Mit getdata() kann aus dem Memory des Testprogramms gelesen werden; mit putdata() kann geschrieben werden. (wird nicht benoetigt)
  • insertcode ist ein shellcode, der einfach /bin/sh ausfuehrt
  • Vorgehensweise
    • Auslesen der Register
    • Schreiben des Shellcodes an den derzeitigen Execution-Instruction-Pointer(rip) (via putdata)
    • Prozess fortfahren
17. März 2010 14:45

Tags:  ·  ·  ·  ·  ·

Blog durchsuchen
(nur öffentliche Einträge)

Willst du auch bloggen?
Kostenlos bloggen bei Spin.de
Diese Seite ist eine auf spin.de gelagerte persönliche Homepage, deren Verantwortlichkeit beim Nutzer liegt.
spin.de ist eine große Online-Community mit Chat, Blogs, Foren, Online-Spielen und vielem mehr.
Deine eigene Homepage mit Blog und Gästebuch

Impressum · Datenschutz · Sitemap