+ W! G" f. S1 P' A8 F2 ~ 1. $hello = Filename on local machine (e.g "/tmp/phpxXuoXG") 1 M% H5 j; l, c7 n
2. $hello_size = Size in bytes of file (e.g 1024) & B' q% L, v% C# b
3. $hello_name = The original name of the file on the remote system (e.g"c:\\temp\\hello.txt") . j s. C2 Z! E( m' Y 4. $hello_type = Mime type of uploaded file (e.g "text/plain") 5 ]# d% h' p2 U( I; }: h9 L8 R6 f5 u6 C! o% K5 G8 D% ~. }3 C! T
然后,PHP程序开始处理根据"$hello"指定的文件。问题在于"$hello"不一定是一个PHP设置的变量,任何远程用户都可以指定它。如果我们使用下面的方式: 6 S* T3 W% L$ T% X) d3 a( ^! E( j7 s
http://vulnhost/vuln.php?hello=/etc/passwd&hello_size=10240&hello_type=text/plain&hello_name=hello.txt * w" _) p6 O+ M4 z* y/ q
4 S* f# f3 T: ]. e就导致了下面的PHP全局变量(当然POST方式也可以(甚至是Cookie)): 6 P( }- S i* A" V' b' D1 k; M' xPHP代码1 l; J. V7 Y& r+ c: s7 S
; }$ X D" y, A
1. $hello = "/etc/passwd" ) \; ^; G) }& M8 v& I O' A
2. $hello_size = 10240 " h( s6 ]/ U& s/ B; \. | 3. $hello_type = "text/plain" & d4 V2 u, W( o% H# ^& F: ? 4. $hello_name = "hello.txt" % m, L& ^. K. i; U- h7 i: _ + ^" j' g( K2 z( i( ], K4 z 上面的表单数据正好满足了PHP程序所期望的变量,但是这时PHP程序不再处理本应在上载者本机上的上载文件,而是处理服务器上"/etc/passwd"(通常会导致内容暴露)文件。这种攻击可以用于暴露任何敏感文件的内容。, W8 Z% \* r8 Q; K1 H* n" ?
- z7 s) L7 l( {$ P6 S- r新版本的PHP使用HTTP_POST_FILES[]来决定上载文件,同时也提供了很多函数来解决这个问题,例如有一个函数用来判断某个文件是不是实际上载的文件。但是实际上肯定有很多PHP程序仍然使用旧的方法,所以也很容易受到这种攻击。 , V" w, V l% j+ {+ a$ e h ; n! J2 \9 v' T1 w4 s/ R作为文件上载的攻击方法的一个变种,我们看一下下面的一段代码: 0 I, \+ s2 ^. b/ E, WPHP代码 3 }' O7 ?0 _+ L0 W. Y; C5 b( D1 ^' c, @" Y0 L4 C
1. <?php / W* K( m9 g8 I8 h0 E$ a" q 2. if (file_exists($theme)) // Checks the file exists on the local system (noremote files) / E4 o+ @, y7 k7 c- J
3. include("$theme"); : U0 i7 P9 N+ s/ [* [
4. ?> 5 \9 \" ~% J- Z
/ u0 t- P2 Z5 S; _- q. Q6 ^# o. ?1 \9 m2 p
如果攻击者可以控制"$theme"的话,很显然它可以利用"$theme"来读取远程系统上的任何文件。攻击者的最终目标是在远程服务器上执行任意指令,但是他无法使用远程文件,因此,他必须得在远程服务器上创建一个PHP文件。这乍看起来好象是不可能的,但是文件上载帮了我们这个忙,如果攻击者先在本地机器上创建一个包含PHP代码的文件,然后创建一个包含名为"theme"的文件域的表单,最后用这个表单通过文件上载把创建的包含PHP代码的文件提交给上面的代码,PHP就会把攻击者提交的文件保存起来,并把"$theme"的值设置为攻击者提交的文件,这样file_exists()函数会检查通过,攻击者的代码也将执行。/ ^* e: b* ?. g v% `# c& j
获得执行任意指令的能力之后,攻击者显然想提升权限或者是扩大战果,而这又需要一些服务器上没有的工具集,而文件上载又一次帮了攻击者的忙。攻击者可以使用文件上载功能上载工具,把她们存在服务器上,然后利用他们执行指令的能力,使用chmod()改变文件的权限,然后执行。例如:攻击者可以绕过防火墙或IDS上载一个本地root攻击程序,然后执行,这样就获得了root权限。 . l! f; k9 O+ `! d% S; C2 a. ~: T5 Q! r" {% n% C6 h
如何通过库文件进行攻击? * ~) O u; T: _* @; D $ N# {, a' Y# [" ^" A正如我们前面讨论的那样,include()和require()主要是为了支持代码库,因为我们一般是把一些经常使用的函数放到一个独立的文件中,这个独立的文件就是代码库,当需要使用其中的函数时,我们只要把这个代码库包含到当前的文件中就可以了。 2 F0 G5 H: [, T9 `# H- [5 d) e2 F; n+ j# b( N; ?
最初,人们开发和发布PHP程序的时候,为了区别代码库和主程序代码,一般是为代码库文件设置一个".inc"的扩展名,但是他们很快发现这是一个错误,因为这样的文件无法被PHP解释器正确解析为PHP代码。如果我们直接请求服务器上的这种文件时,我们就会得到该文件的源代码,这是因为当把PHP作为 Apache的模块使用时,PHP解释器是根据文件的扩展名来决定是否解析为PHP代码的。扩展名是站点管理员指定的,一般是".php", ".php3"和".php4"。如果重要的配置数据被包含在没有合适的扩展名的PHP文件中,那么远程攻击者很容易得到这些信息。 % X+ @9 ?. {+ f7 T- S+ z" D2 W) o$ L0 d1 B( X6 F) `( A* E6 P
最简单的解决方法就是:给每个文件都指定一个PHP文件的扩展名,这样可以很好的防止泄露源代码的问题,但是又产生了新的问题,通过请求这个文件,攻击者可能使本该在上下文环境中运行的代码独立运行,这可能导致前面讨论的全部攻击。. E* z$ g3 I% b' s: c( }( D" }
^6 e/ i9 h' M1 w
下面是一个很明显的例子: ( l3 t; G& i3 F* K2 L- L( cPHP代码0 \% H+ T' `" ]& e
3 @9 }7 I) U/ g/ Y- ?% ?( M
1. In main.php: : S% _0 u# Y2 F" g( K: F4 s4 I 2. <?php ; L0 n$ e: U1 \% O* P! Z+ |* i 3. $libDir = "/libdir"; 0 X% i- N! [. G) R: C5 C1 X) M
4. $langDir = "$libdir/languages"; _1 a9 Y% |- e+ y$ H* R5 L 5. ... j! l) }+ o, V$ t# X: D# z- G
6. include("$libdir/loadlanguage.php": / I2 c- G3 o/ N, D, }& t, u5 c
7. ?> ' ?) Z7 g/ s& E1 Y6 S 8. + _% y3 C+ [! d1 V
9. In libdir/loadlanguage.php: # W- k, P! E8 K
10. <?php . g5 q( U) i" V4 i, N% U, z
11. ... % q, ]+ l1 l5 C8 ]
12. * g8 F$ e" w4 Y$ |7 ] 13. include("$langDir/$userLang"); ; j, a) U' E. g' s
14. ?> 4 Y" z) n2 p8 ^& R
; M" l2 b, T2 G* f4 c 当"libdir/loadlanguage.php"被"main.php"调用时是相当安全的,但是因为"libdir /loadlanguage"具有".php"的扩展名,因此远程攻击者可以直接请求这个文件,并且可以任意指定"$langDir" 和"$userLang"的值。' |3 a. ^3 U' J& B' [$ ?$ B! G
1 Q+ D4 g4 d: b$ M( c2 |; T0 K
如何通过Session文件进行攻击?2 v" l6 s4 p7 y+ I- O- H9 U
! b* u9 E3 K& r, DPHP 4或更新的版本提供了对sessions的支持,它的主要作用是在PHP程序中保存页与页之间的状态信息。例如,当一个用户登陆进入网站,他登陆了的这个事实以及谁登陆进入这个网站的相关信息都将被保存在session中,当他在网站中到处浏览时,所有的PHP代码都可以获得这些状态信息。 $ N0 E+ N$ ~, ]9 O! O. h : r8 g! `/ C1 d! N$ Y, K事实上,当一个session启动时(实际上是在配置文件中设置为在第一次请求时自动启动),就会生成一个随机的"session id",如果远程浏览器总是在发送请求时提交这个"session id"的话,session就会一直保持。这通过Cookie很容易实现,也可以通过在每页提交一个表单变量(包含"session id")来实现。PHP程序可以用session注册一个特殊的变量,它的值会在每个PHP脚本结束后存在session文件中,也会在每个PHP脚本开始前加载到变量中。下面是一个简单的例子: 3 S3 m: E( L- s6 t% |" M/ GPHP代码 ( ~; T$ W" X. n* j2 T* w2 t2 A& b' e" @8 `; @- h# r7 L
1. <?php ?# q+ f1 H8 K; A$ p 2. session_destroy(); // Kill any data currently in the session / G B* ]/ |" O# n5 [2 ~! E 3. $session_auth = "shaun"; - X' \7 r9 Y4 M; h& f3 } 4. session_register("session_auth"); // Register $session_auth as a session variable + @8 A3 V- H4 K6 `4 H5 b- J/ i9 U* q
5. ?> 4 ?# ]/ F8 |" r0 z