SV.USAGERULES.PERMISSIONS

权限提升风险

某些函数需要特殊的权限才能正常工作,或者只能由某些用户或某些组的成员(比如本地管理员)运行。另一些函数则需要用户帐户启用特定的权限,比如访问系统资源的权限。例如,在网络服务中,分配具有权限的 TCP 或 UDP 端口通常需要超级用户权限,普通用户权限无法满足要求。

虽然可能仅是临时需要提升的权限,但许多程序不会降低提升的权限,而会继续以具备不必要的根权限的状态运行。

为了降低权限,需要进行一个流程:使用 seteuid() 和 setegid() 系统调用来将其自己的有效用户和组 ID 设为权限较少的用户或组,以临时降低它们的权限,或者如果使用 setuid() 和 setgid() 则可永久降低。

当低权限程序需要执行高权限的操作(如打开具有权限要求的文件)时,程序需要使用 setuid 来提升权限。setuid 功能允许以具备根权限的用户的身份来启动可执行程序。同样的,程序通常都无法在完成高权限操作之后立即还原到其较低的权限状态。

SV.USAGERULES.PERMISSIONS 检查器会查找通过调用将权限提升至 setegid()、seteuid()、setuid()、setregid() 和 setreuid() 以及 AddAccessAllowedAce() 或 AddAccessAllowedAceEx() 的实例,并将它们标记出来以进行检查。

漏洞与风险

为了降低未授权代码取得控制权限的可能性,系统应当以必要的最低权限运行。应用程序如需调用需要特定权限的函数,则可能会让系统处于容易遭受黑客攻击的状态。以额外的权限运行可能会禁用操作系统或环境中的常规安全性检查,从而造成在以提升的权限进行操作期间,之前存在的弱点变成安全漏洞。应用程序应该设计为仅在短时间内以提升的权限运行,也应该通知用户相关的安全性影响。

缓解与预防

确定您的应用程序需要在何种帐户类型下运行的第一步是检查应用程序会用到的资源以及应用程序会调用的需要权限的 API。您可能会发现应用程序或应用程序的大部分都不需要管理员权限。《编写安全代码》(作者为 Michael Howard 和 David LeBlanc)对如何执行此类评估进行了精彩的讲解,强烈推荐您仔细阅读。

为了降低暴露在恶意攻击下的可能性,请使用以下方法:

  • 尽可能随时以权限较少的帐户来运行,尽可能晚地提升权限,并尽快降低它们。一种方法是使用 PrivilegeCheck 来确定令牌中启用了什么权限。如果可用的权限不足以进行当前操作,您可以禁用该代码并要求用户以具有管理员权限的帐户登录。
  • 确保通过用户名和密码对用户进行充分的身份验证。您可以通过调用 CredUIPromptForCredentials (GUI) 或 CredUICmdLinePromptForCredentials (command line) 来获取用户名和密码,从而对用户进行身份验证。
  • 模拟用户。在高权限帐户(比如系统)下启动的进程可以通过调用 ImpersonateLoggedOnUser 或类似的模拟函数来模拟用户帐户,从而降低权限级别。对 RevertToSelf 的调用将注入到线程中,从而将进程返回为原始的系统权限。
  • 隔离需要权限的代码。将需要管理员权限的函数分解为多个独立的应用程序。
  • 通过正确的吊销顺序来降低权限。在使用 setuid 和 setguid 程序的情况下,请务必先降低组级别的权限,然后再降低用户级别的权限。
  • 确保提升的权限已成功降低。请务必检查以下调用的返回值:
    • setuid
    • setguid
    • RpcImpersonateClient
    • ImpersonateNamedPipeClient
    • ImpersonateSelf
    • SetThreatToken
    • ImpersonateLoggedOnUser
    • CoImpersonateClient
    • ImpersonateAnonymousToken
    • ImpersonateDdeClientWindow
    • ImpersonateSecurityContext

当程序正在操纵权限时,任何此类函数的调用失败都将导致无法降低权限。如果没有检查返回值并采取相应的操作,程序可能继续以提升权限的帐户进行操作。

安全培训

应用程序安全培训材料由 Secure Code Warrior 提供。