PHP 日本大会 2024

posix_setuid

(PHP 4, PHP 5, PHP 7, PHP 8)

posix_setuid设置当前进程的 UID

描述

posix_setuid(int $user_id): bool

设置当前进程的真实用户 ID。这是一个特权函数,需要系统中相应的权限(通常是 root)才能执行此函数。

参数

user_id

用户 ID。

返回值

成功返回 true,失败返回 false

示例

示例 #1 posix_setuid() 示例

此示例将显示当前用户 ID,然后将其设置为不同的值。

<?php
echo posix_getuid()."\n"; //10001
echo posix_geteuid()."\n"; //10001
posix_setuid(10000);
echo
posix_getuid()."\n"; //10000
echo posix_geteuid()."\n"; //10000
?>

参见

添加注释

用户贡献的注释 7 条注释

Leigh
10 年前
请注意,在 Unix 系统上,如果目标用户没有有效的 shell,某些 PHP 函数(例如:tempnam)将无法正常工作。

$ grep www-data /etc/passwd
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

$ cat test.php
#!/usr/bin/php -q
<?php
$info
=posix_getpwnam("www-data");
$id=$info["uid"];

$file=tempnam("/tmp","something");
print
"PRE SetUID: $file\n";

$SETUID=posix_setuid($id);

$file=tempnam("/tmp","something");
print
"POST SetUID: $file\n";
?>

$ sudo ./test.php
PRE SetUID: /tmp/somethingrsb1qZ
POST SetUID
TheWanderer
17 年前
在许多 UNIX 系统(在 Debian GNU/Linux 上测试过)上,SUID 对脚本是禁用的,仅对二进制文件有效。如果需要 setuid,则必须使用运行 setuid() php 脚本的包装器二进制文件。这是一个示例

$ nano suexec.cpp
#include <stdlib>
using namespace std;
int main()
{
system("php /home/php/php_user.php");
return 0;
}

$ g++ -o suexec suexec.cpp
$ sudo chown root:root suexec
$ sudo chmod 4755 root

然后我们创建一个简短的 PHP 脚本以设置进程 uid(您应该已经知道如何做到这一点)。甚至不要尝试在 php.ini 中试验 auto_prepend_file,它无法按预期工作。
fm at farhad.ca
17 年前
当您从 root 用户使用 posix_setuid 切换到其他用户时,根据其权限,您将无法访问 root 用户拥有的文件。例如,如果您更改了进程的所有者,但仍然需要以 600 权限打开 root 用户拥有的文件进行读写,您将收到权限被拒绝的错误。
有一些方法可以解决这个问题(例如 Unix 套接字或 tcp 守护程序等),但可能最简单的方法是

在更改进程所有权之前打开文件,将文件指针保存在全局变量中,并在更改所有权后使用它。

例如,假设 /root/test_file 是由 root:root 拥有且具有 600 权限的文件,并且您正在 root 用户下运行此脚本。此代码将不起作用

<?php
// 更改进程所有者为 nobody
posix_setgid(99);
posix_setuid(99);

$fd = fopen('/root/test_file','a');
fwrite($fd,"some test strings");
fclose();

?>

但这个会起作用

<?php
$fd
= fopen('/root/test_file','a');

// 更改进程所有者为 nobody
posix_setgid(99);
posix_setuid(99);

fwrite($fd,"some test strings");
fclose();

?>

希望这对某些人有所帮助。

[在 CentOS 5 - Linux 2.6.x - PHP 5.2.x 上测试]
reuben @ nospam me
17 年前
针对上面提倡在用 C 编写的 setuid 程序中使用 system() 的注释,这通常不利于安全。

您应该使用 execl() 等标准库调用,因为 system() 可以通过 SHELL、IFS 和可能的其他变量被操纵以执行错误的操作。
hpaul/at/abo/dot/fi
18 年前
似乎如果尝试将 uid 更改为已激活的用户(即使您不是 root),此函数也会返回 true。

在某些情况下可以为您节省一个 if 语句。
simon at dont-spam-me-pleease dot simonster dot com
22 年前
这是一些运行 setuid PHP 脚本的 Perl 代码。只需将其放入 CGI 中,使该 CGI 设置为 setuid 并可执行,然后在通常调用 PHP 脚本的地方调用 CGI。

#!/usr/local/bin/perl

## Perl包装器,用于以setuid方式执行PHP脚本
## © 2002 Simon Kornblith
## 需要PHP CGI

## 使UID = EUID(以便PHP可以运行setuid的system()和exec())
$< = $>;
## 设置此路径,以便我们不会受到攻击
$ENV{'PATH'} = "/home/httpd/cgi-bin/ssl/admin";
## 打开PHP脚本(必须以#!/usr/local/bin/php或类似开头,并且
## 可执行)
open(STDOUT, "| /home/httpd/cgi-bin/ssl/admin/new.php");
## 将STDIN写入PHP脚本
print while <STDIN>;
rjmooney at syr dot edu
21年前
对于简单的操作,您可以轻松创建权限分离机制来执行需要提升权限的命令。

例如,在创建文档存储库时,我需要根据用户的登录名提供对某些目录和文件操作的访问权限。向Web服务器提供用户可能需要访问的所有目录的访问权限是不现实且不安全的,因此我创建了一个setuid()脚本为我执行所需的操作。

代码片段演示了这一点

<?

//
// main.php
//

// 执行特权stat()
function privsep_stat($path)
{
// 调用权限分离程序,请求指定路径的stat
$serialized_result = exec("/path/to/privsep.php stat " . $path, $oa, $return_code);
if ($return_code != 0)
{
return false;
}

// 返回反序列化的对象
return unserialize($serialized_result);
}

// 获取我们作为Web服务器用户无权访问的文件的文件统计信息
$st = privsep_stat("/private_directory/private_file");
print_r($st);

?>

privsep.php看起来像这样

#!/usr/local/bin/php
<?

//
// privsep.php
//

// 不要允许从Web直接运行此脚本
if (isset($_SERVER['REQUEST_METHOD']))
{
print "<br>此程序不应直接从WWW运行。\n";
return 1;
}

// TODO:在此处添加您的参数验证

// 请求了stat
if ($argv[1] == "stat")
{
// 重置stat()缓存
clearstatcache();

// 原始用户ID
$original_uid = posix_get_uid();

// 将我们的真实用户ID设置为root
$success = posix_setuid(0);
if (!$success)
{
print "Error: Cannot setuid().\n";
return 1;
}

// 存储文件统计信息
$st = stat($argv[2]);

// 将真实UID恢复为调用用户ID
$success = posix_setuid($original_uid);
if (!$success)
{
print "Error: Cannot setuid().\n";
return 1;
}

// 也放弃有效UID
$success = posix_seteuid($original_uid);
if (!$success)
{
print "Error: Cannot seteuid().\n";
return 1;
}

// 序列化结果并打印
$result = serialize($st);
print $result;

// 成功!
return 0;
}
?>

最后,privsep.php的权限配置如下

# chown root:wheel privsep.php
# chmod 4755 privsep.php

并且看起来像这样

-rwsr-xr-x 1 root wheel 1000 11月 1 00:00 privsep.php

为了减轻任何成功的攻击,最好将privsep.php 保持在文档根目录之外。

此方法可以扩展到其他功能。使用风险自负。
To Top