Overview
error_log($content . "\r\n", 3, $this->logFile);,该代码将未经验证的用户输入写入日志。攻击者可以利用这一行为来伪造日志条目或将恶意内容注入日志。
Details
在以下情况下会发生 Log Forging 的漏洞:
1. 数据从一个不可信赖的数据源进入应用程序。
2. 数据写入到应用程序或系统日志文件中。 在这种情况下,数据通过?error_log() 记录下来。 为了便于以后的审阅、统计数据收集或调试,应用程序通常使用日志文件来储存事件或事务的历史记录。根据应用程序自身的特性,审阅日志文件可在必要时手动执行,也可以自动执行,即利用工具自动挑选日志中的重要事件或带有某种倾向性的信息。 如果攻击者可以向随后会被逐字记录到日志文件的应用程序提供数据,则可能会妨碍或误导日志文件的解读。最理想的情况是,攻击者可能通过向应用程序提供包括适当字符的输入,在日志文件中插入错误的条目。如果日志文件是自动处理的,那么攻击者就可以通过破坏文件格式或注入意外的字符,从而使文件无法使用。更阴险的攻击可能会导致日志文件中的统计信息发生偏差。通过伪造或其他方式,受到破坏的日志文件可用于掩护攻击者的跟踪轨迹,甚至还可以牵连第三方来执行恶意行为 。最糟糕的情况是,攻击者可能向日志文件注入代码或者其他命令,利用日志处理实用程序中的漏洞 。
示例: 下列 Web 应用程序代码会尝试从一个请求对象中读取整数值。如果数值未被解析为整数,输入就会被记录到日志中,附带一条提示相关情况的错误消息。
<?php $name =$_GET['name'];
$logout =$_GET['logout'];
if(is_numeric($logout)) { ... }
else {
trigger_error("Attempt to log out: name: $name logout: $val");
} ?>
如果用户为 logout 提交字符串“twenty-one”,而且他可以创建一个名为“admin”的用户,则日志中会记录以下条目: PHP Notice: Attempt to log out: name: admin logout: twenty-one 但是,如果攻击者可以创建用户名 "admin+logout:+1+++++++++++++++++++++++",则日志中将记录以下条目: PHP Notice: Attempt to log out: name: admin logout: 1 logout: twenty-one
Recommendations
使用间接方法防止 Log Forging 攻击:创建一组与不同事件一一对应的合法日志条目,这些条目必须记录在日志中,并且仅记录该组条目。要捕获动态内容(如用户注销系统),请务必使用由服务器控制的数值,而非由用户提供的数据。这就确保了日志条目中绝不会直接使用由用户提供的输入。
在某些情况下,这个方法有些不切实际,因为这样一组合法的日志条目实在太大或是太复杂了。这种情况下,开发者往往又会退而采用执行拒绝列表方法。在输入之前,拒绝列表会有选择地拒绝或避免潜在的危险字符。然而,不安全字符列表很快就会不完善或过时。更好的方法是创建一个字符列表,允许其中的字符出现在日志条目中,并且只接受完全由这些经认可的字符组成的输入。在大多数 Log Forging 攻击中,最关键的字符是“\n”换行符,这样的字符决不能出现在日志条目允许列表中。在使用默认的 trigger-error 函数时,在日志文件中将不会显示换行符,但是使用 set_error_handler 函数可能会覆盖默认的行为。覆盖默认的函数时,务必谨记此建议。