王然的烦恼--她很精通linux啦

版权声明:本文为博主原创,无版权,未经博主允许可以随意转载,无需注明出处,随意修改或保持可作为原创! https://blog.csdn.net/dog250/article/details/5692612

宝贝王然最近十分想搞嫁祸于人的恶作剧!

一台linux主机上建立了一个用户wangran,一共有两个人王然和王其知道用户wangran的密码pw,其中王其还知道root密码而王然不知道,为了安全起见,远程用户都不得用root直接登录主机,也就是王然和王其只能通过wangran这个用户来登录,王然自认为是linux高手,她当然知道他们远程登录的终端是/dev/pts/n,n从0开始递增,而且她也知道/dev/pts/n的权限就是通过终端登录的用户也就是wangran的权限,任何su的用户都不会改变终端文件的权限,由于她不知道root密码,然而她又想做一些XX,于是她想出了一个嫁祸于人的办法,王然开始工作,她的最终目的是在王其登录的终端上执行root命令,设想王其登录的终端是/dev/pts/y,而王然的登录终端是/dev/pts/x,王然想一定能在/dev/pts/y上执行命令,因为她有权操作/dev/pts/y,毕竟他俩都是通过一个username登录的。
     起初,王然把事情想简单了,她首先来了一个用过linux的人都会的方式:
wangran@DROP:~$echo ls > /dev/pts/y    #当然不能用halt命令来测试了,所以用ls。
为了得知结果,王然让王其看屏幕有何变化,王其很郁闷,不解为何突然屏幕上被写了一个ls,而王然只说这只是一个恶作剧,以显示自己水平的高超,她是断然不会把最终目的告诉王其的,于是接下来的工作是王其在帮着王然完成的。只写了一个ls,没有什么用,是不是没有写回车符号呢,于是精通vt100终端的王然又来了一个:
wangran@DROP:~$echo -e 'pwd^M' > /dev/pts/y #行倒是换了,可是还是没有执行ls
于是王然拿出了strace,让王其在屏幕执行:strace bash,王其被出来的一大堆看不懂的信息给搞懵了,于是离开了座位,把两个终端全部让给了王然,此时王然当然可以敲入passwd命令修改root密码,可是作为一名黑客,这样做不雅。
     在王其的终端上:strace bash正在运行中:
...
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0, "l", 1)                         = 1
write(2, "l", 1l)                        = 1
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0, "s", 1)                         = 1
write(2, "s", 1s)                        = 1
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0, "/r", 1)                        = 1
write(2, "/n", 1
)                       = 1
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
...
stat64(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
...
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x4005e968) = 8396
...
waitpid(-1, ...(ls命令的结果)...
...
只要你敲入一个字符,strace就会先从标准输入read出来,在从标准输出write回去,如果在别的终端上通过echo的方式往另一个终端写东西,无论如何都是不能被该目的终端解释为键盘输入的,因为echo只是将字符写入了输出缓冲区,最终将送往屏幕,所以你只能看到你写入的信息,而你无法将字符从另一个终端送入该终端的输入缓冲区,在tty终端以及真实的终端只有键盘的敲击才能做到往输入缓冲区送信息,而在诸如/dev/pts/n或者串口之类的终端,你必须遵循其“线路规程”才可以,比如telnet终端,你在远程敲入了字母l,该字母l将会按照传输线路以及应用协议的规则被编码,然后主机在接收到该编码后会同样按照线路规程和应用协议将数据解码,最终将字母l送入终端的输入缓冲区。这个过程是复杂的,可爱的王然决定将黑客精神发扬光大,因此她将这个过程呈现出来。
     以在windows机器上ssh一台linux主机为例,我们执行strace -p pid(随意一个sshd进程号),缩略的结果如下(以#标识注释):
...
read(4, "/3166/213/310/..."..., 16384) = 5 #从文件标识符4读取数据(加密数据)
select(11, [4 6 9 10], [8], NULL, NULL) = 1 (out [8]) #文件8可写
...
write(8, "l", 1) = 1  #写入文件8字符"l"
...
select(11, [4 6 9 10], [], NULL, NULL)  = 1 (in [9]) #文件9可读
...
read(9, "l", 16384) = 1   #文件9中读取字符l
select(11, [4 6 9 10], [4], NULL, NULL) = 1 (out [4])
...
write(4, "a/313/3732/..."..., 36) = 36
select(11, [4 6 9 10], [], NULL, NULL)  = 1 (in [4])
...
read(4, "/3./364/352/2..."..., 16384) = 52
select(11, [4 6 9 10], [8], NULL, NULL) = 1 (out [8])
...
write(8, "s", 1) = 1
...
select(11, [4 6 9 10], [], NULL, NULL)  = 1 (in [9])
...
read(9, "s", 16384) = 1
select(11, [4 6 9 10], [4], NULL, NULL) = 1 (out [4])
...
write(4, "/20zSMS/215..."..., 36) = 36
select(11, [4 6 9 10], [], NULL, NULL
...
以上是strace的输出,可是文件描述符4,8,9到底是什么呢?这还得需要lsof命令帮忙,下面是lsof -p pid(sshd的pid)的缩略结果
-----------------------------lsof sshd
...:/usr/src/linux# lsof  -p 8793
COMMAND  PID   USER   FD   TYPE     DEVICE    SIZE     NODE NAME
...
sshd    8793 zyw    3u  unix 0xc33c7ac0            12870 socket
sshd    8793 zyw    4u  IPv6      12857     TCP host1:ssh->host2 #4是一个套接字
sshd    8793 zyw    5u  unix 0xf413be40            12872 socket
sshd    8793 zyw    6r  FIFO        0,7            12873 pipe
sshd    8793 zyw    7w  FIFO        0,7            12873 pipe
sshd    8793 zyw    8u   CHR        5,2         23921462 /dev/ptmx #8和9都是终端
sshd    8793 zyw    9u   CHR        5,2         23921462 /dev/ptmx
...
知道了文件描述符代表的文件,下一步就是分析sshd的执行过程了,可以从strace的输出看到,首先从网络读取了一段加密的数据,然后将之写入了/dev/ptmx,然后将又从相同的文件读出了刚刚写入的相同字符,就像回显一样,究竟sshd将数据写到了哪里?如果输入l和s之后敲回车,那么当前目录的文件就会显示出来,这又是为什么?
     简单说来,终端是一个古老的东西,大体上分为两类,第一类是真实的终端,比如通过串口连接到主机的终端或者slip线路终端等等,这类终端其实很简单,本质上就是一条网线,你可以将其线路规程理解成物理层协议,看一段slip的tty线路规程代码就知道了,每个线路规程都有一个receive_buf方法,负责从驱动接收数据并且交给线路规程来处理:slip_receive_buf即是其slip的receive_buf方法,其中调用netif_rx将数据按照线路规程解码后交给网络协议栈。;第二类终端当然就是伪终端了,其中又分为两个小类,一类是console终端,也就是/dev/ttyn(n是一个数)之类的,它们更简单,这类终端的读缓冲区和键盘之类的输入设备相联系,而写缓冲区和屏幕或者打印机之类的输出设备相联系,只要你敲击了键盘,数据就会进入tty系列的读缓冲,而往tty写数据,则会显示到输出设备上,另一类网络伪终端,它们都是成对的,即/dev/ptmx和/dev/pts/n(n是一个数),因此如果你往终端文件写一个字符,那么该字符将从另一端被读出,反之亦然,因此,王然告诉大家,sshd将数据写入了ptmx,然后数据就进入了pts/n的读缓冲区,那么谁来读取呢?答案是shell,同样的执行一下lsof -p pid(一个ssh终端bash的pid),就会发现其标准输入,输出都是/dev/pts/n,然后strace -p pid(一个ssh终端bash的pid),就会发现bash先从pts/n读取数据,然后再往其写入相同的数据,显然读取的数据是sshd写入ptmx的,而将相同的数据写入pts/n,sshd则可以从ptmx读取,然后通过套接字回显到远程的windows终端之上,整个过程是sshd-ptmx和bash-pts/n在配合,windows方面只是一个显示作用。
     王然的解说结束了,应该还算详细,说归说,王然还是总忘不了她的最终任务,因此使出了最后一招,那就是写一个c文件,main函数如下:
int fd = open("/dev/pts/0", O_RDWR);
ioctl(fd,   TIOCSTI,   "l ");
ioctl(fd,   TIOCSTI,   "s ");
ioctl(fd,   TIOCSTI,   "/n ");
return 0;
以wangran用户执行之,得到了令人失望的权限错误,看了内核代码,TIOCSTI的ioctl会调用tiocsti,在tiocsti函数中有:
static int tiocsti(struct tty_struct *tty, char __user *p)
{
    char ch, mbz = 0;
    struct tty_ldisc *ld;
    if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
        return -EPERM; //就是在此出的权限错误
    ...
    ld->receive_buf(tty, &ch, &mbz, 1); //如果是root用户或者使用当前终端,那么就可以将数据放入读缓冲区了。
    ...
}
可是王然毕竟不是王其,怎么办?除了放置木马就彻底没有办法了,到头来,还是没有找到linux的漏洞啊!可是,且慢!将权限检查放到内核这样好吗?王然觉得很不好,为何不在用户执行su other的时候将其对应终端的权限修改成other的权限呢?答案在哪?答案在写内核的人对写shell的人不信任?王然觉得是这样的,可是不信任的代价太大了,机制和策略不再分离...明显是用户空间的问题为何非要内核来做呢?于是王然修改了内核源码,将tiocsti中那个判断去掉,同时修改了bash的源码,一旦用户su成了别的用户,其终端也就成了别的用户权限,如果su成了root,则wangran用户根本就无权限操纵pts/n,内核不必再参与用户空间的授权事务了,正如内核不管用户密码的保管一样,如果一个shell在su的时候没有改变终端的权限,那么有一种需求是用户希望使用该shell在别的终端上执行类似QQ或者MSN远程协助的功能。
     最后,王然要总结一下在/dev/pts/m通过echo ls > /dev/pts/n为何不能在pts/n上执行ls,当她在windows上的一个ssh终端执行echo ls > /dev/pts/n的时候,整个命令行通过ssh协议传输到了linux主机的sshd进程,然后sshd进程将之写入到/dev/ptmx,最终/dev/pts/m上的bash进程的read返回sshd写入ptmx的数据,也就是整个命令行,然后bash执行之,最终将输出重新定向到了/dev/pts/n,写入pts/n就意味着数据可以从ptmx中被读出,确实地,数据被和pts/n相关的sshd进程从ptmx中读出了,然后通过网络将ls+/n这些字符显示到了windows上和/dev/pts/n相关的ssh终端上,可见至始至终,/dev/pts/n上的bash并没有读取任何数据,而若想使之执行ls命令就必然要使得bash读出ls/n,最终写入/dev/pts/n的数据仅仅显示到了windows机器相应的ssh终端而已。我们知道,一般终端输入都和输入设备相关,ssh终端虽然拉远了linux主机和终端的距离,但是不可否认若想在/dev/pts/n上执行ls,则此ls必然需要在windows机器键盘输入,并且是和/dev/pts/n相关的ssh终端窗口的输入,只有这样,数据才能一步步经过windows机器->linux的sshd->linux的/dev/ptmx->/dev/pts/n->bash执行后再通过bash->/dev/pts/n->linux的/dev/ptmx->linux的sshd->windows机器回显过来。

没有更多推荐了,返回首页