【问题标题】:How does one pass includes to execvp that's running a perl script?一次通过如何包含运行 perl 脚本的 execvp?
【发布时间】:2018-09-06 10:58:19
【问题描述】:

RHEL6

在 C 中,如何通过包含 execvp 来运行 perl 可执行文件? (这是一种在污点模式开启时将缺失的 PERL5LIB 翻译替换为 @INC 的方法)

这个问题正在寻找由 mob 提供的答案的有效实现... Why does @INC change when setgid-bit of C wrapper around perl script change?.

我在一个使用 execvp 调用 perl 的 C 程序中包装了一个 perl 脚本执行。因为启用了污点模式,@INC 将不会加载 $PERL5LIB 的内容,所以我使用“-I”将路径传递给 perl 可执行文件。 perlsec 说这是可能的(参考 mob 在上面的注释中提供的答案)。

我这样做的尝试失败了。我的 C 程序尝试将 perl 可执行文件作为第一个参数运行到 execvp,然后将 perl 脚本作为参数列表的一部分。这些包含已成为 perl 脚本可执行语句的一部分。这是示例...

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
  char **args;  /*args will be passed to execvp.  prepend perl executable*/

  args=malloc(3*sizeof(char*));

  args[0]=malloc(sizeof("/tools/bin/perl -I/proj/perlmods")+1);
  strcpy(args[0],"/tools/bin/perl -I/proj/perlmods");

  args[1]=malloc(sizeof("/home/me/the.pl")+1);
  strcpy(args[1],"/home/me/the.pl");

  args[2]=NULL;

  execvp("/tools/bin/perl",args);

}

perl 脚本很简单……

use html_mail;
print "In my.pl\n";
exit;

/proj/perlmods 有 html_mail.pm

在 linux 提示符下使用 -I works 直接执行 perl...

% /tools/bin/perl -I /proj/perlmods /home/me/my.pl
In my.pl

但运行 C 二进制文件不会。它抱怨找不到 perl 模块。但是,如果将 /proj/perlmods 放在 PERL5LIB 中,那么它确实可以工作。所以我认为 args 就 args[1] 中的 perl 脚本而言是好的。

我相信我放入 args 的 include 语句被忽略了。如果我将它作为后缀粘贴到 execvp 中的 perl 可执行调用...

  execvp("/tools/bin/perl -I /proj/perlmods",args);

...C 程序失败,所以我认为它不属于那里。如果我将包含作为 perl 脚本调用的一部分....

  strcpy(args[1],"/home/me/the.pl -I /proj/perlmods");

...它失败了。如果我将它作为 args (args[3]) 的新元素添加,它只是作为 arg 传递给 perl 脚本并且没有任何影响。

我查看了 execvpe,但这看起来只是一种设置环境变量的方法。

问题是,如何实际将包含信息传递给 execvp?

【问题讨论】:

  • 如果execvp 失败,则设置errno。在execvp 之后添加perror("execvp") 以查看失败的原因。
  • 还要注意sizeof("/home/me/the.pl") == strlen("/home/me/the.pl") + 1,因为"/home/me/the.pl"是一个字符串字面量。

标签: c perl


【解决方案1】:

您需要将每个参数作为args 中的单独条目。试试这个:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    char *args[4];       /* args will be passed to execvp.  prepend perl executable */

    args[0] = "/tools/bin/perl";
    args[1] = "-I/proj/perlmods";
    args[2] = "/home/me/the.pl";
    args[3] = NULL;

    execvp(args[0], args);

}

【讨论】:

  • 我试过这个。如果未设置 C 二进制文件上的粘滞位,它会起作用,但如果设置则会失败。如果已设置,则它会在污点模式下运行,尽管有文档,但仅使用 -I 运行 perl 可执行文件还不够好。这可能是另一个安全障碍。使用“use lib”显式列出 perl 源中的库确实有效,但会破坏 PERL5LIB 的动态性。在这一点上,我觉得我正在尝试绕过安全障碍。我想我会退后一步,寻找一个不同的角度。感谢所有的帮助!希望这对其他人有所帮助。
  • @daveg,正如我之前指出的,您说的是 setuid 位,而不是 setuid 位。
  • 我以前必须这样做(例如 setuid 启动 perl 脚本)。当您设置 setuid 程序时(例如,将所有权更改为 root 并执行chmod 6741 ...),它只会将 euid(有效用户 id)设置为 0/root 和 not uid(真实用户 id)。 perl 将看到 uid!=0 和 euid==0 并设置污点模式。让启动程序执行setuid(0)setgid(0),以便perl 将所有内容视为root 并且不会抱怨。不确定,但是 setuid 程序可能会擦除一些环境,因此您可能需要启动器来设置环境变量(例如 PERL5LIB 等)
  • (呃,我的上一条评论应该是“你说的是 setuid 位,而不是粘性位”)
  • @Craig Estey,试图欺骗 Perl 使用调用者的 PERL5LIB 是一个糟糕的主意。它应该被忽略,因为不忽略它将允许调用者以root身份运行任意代码。
【解决方案2】:

如果你使用-I,你就做错了。脚本的执行者不必关心脚本库的位置。

  • 如果/proj/perlmods 被多个应用程序使用,您应该将以下内容添加到您的登录脚本中:

    export PERL5LIB=/proj/perlmods
    
  • 如果/proj/perlmods 仅由该 Perl 程序使用,则将以下内容添加到 Perl 程序中:

    use lib qw( /proj/perlmods );
    

那么,C程序就变成了

char *args[2];                  # Or char **args = malloc(2 * sizeof(char*));
args[0] = "/home/me/the.pl";    # No need to duplicate the string; just copy the ptr.
args[1] = NULL;

execvp(args[0], args);

而不是

char *args[4];                  # Or char **args = malloc(4 * sizeof(char*));
args[0] = "/tools/bin/perl";
args[1] = "-I/proj/perlmods";
args[2] = "/home/me/the.pl";
args[3] = NULL;

execvp(args[0], args);

【讨论】:

  • 这可行,但是 perl 源中库的静态定义不适合我们使用 $PERL5LIB 作为在环境中设置的动态库集的模型。使用可以将 $PERL5LIB 的内容作为包含内容回显的 C 包装器(理论上)是可行的,但似乎它们设置了其他安全障碍来防止这种情况发生。
  • @daveg,不不不,不要使用PERL5LIB。您将让初始用户以 setuid 用户身份运行任何代码!您绝对应该在 setuid 情况下使用use lib。 (并摆脱 C 包装,正如我之前提到的。)
猜你喜欢
  • 2014-08-27
  • 2013-09-05
  • 2016-04-14
  • 2016-01-27
  • 1970-01-01
  • 2011-04-27
  • 2016-05-05
  • 2014-06-20
  • 1970-01-01
相关资源
最近更新 更多