Sie sind auf Seite 1von 41

http://www.sektioneins.

de

Bytekit - Ein Werkzeug zur Arbeit


mit PHP Bytecode
Stefan Esser <stefan.esser@sektioneins.de>

Mai 2009 - Berlin


Wer ich bin?

Stefan Esser
• aus Köln / Deutschland

• Informations Sicherheit seit 1998

• PHP Kern Entwickler seit 2001

• Month of PHP Bugs und Suhosin

• Leiter von Forschung und Entwicklung bei SektionEins GmbH

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  2
Teil I
Einführung

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  3
Einführung

• PHP Bytecode
• ist die compilierte Form von PHP
PHP Skript
• gibt guten Einblick in Funktionsweise von PHP
PHP
• erlaubt Analysen auf der Ausführungsebene Quelltext

• rückt mehr und mehr in den Blickpunkt


Compiler
Optimizer - Static Analysis - Dynamic Analysis
PHP
Bytecode

• Existierende Tools für uns unzureichend


• vld - Vulcan Logic Disassembler Executor
• parsekit - Zugriff auf Bytecode Rohdaten

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  4
Bytekit

• PHP Erweiterung
• gibt Zugriff auf PHP Bytecode Rohdaten (ähnlich parsekit)
• enthält PHP Bytecode Disassembler (ähnlich vld)
• gibt Control Flow Informationen
• Zuordnung zu Basic Blocks
• Control Flow Graph
• hilft bei Visualisierung

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  5
Teil II
Bytekit - API

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  6
Funktionen

• Bytekit bietet bisher 3 API Funktionen an


• int bytekit_get_baseaddress()
• void bytekit_set_baseaddress(int baseaddress)
• array bytekit_disassemble_file(string filename[, array
&errors[, mixed options]])

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  7
Konstanten

• Bytekit bietet eine Vielzahl von Konstanten


• Parameter Konstanten - BYTEKIT_SHOW_SPECIALS
• Interne Konstanten - BYTEKIT_OP1_JMPADDR
• Zend Interne Konstanten - BYTEKIT_IS_CV
• Zend Engine Opcodes - BYTEKIT_INCLUDE_OR_EVAL

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  8
bytekit_get_baseaddress()

int
bytekit_get_baseaddress()

Abfragen der aktuellen Basisadresse für den

Zend Engine Disassembler

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  9
bytekit_set_baseaddress()

void
bytekit_set_baseaddress(int baseaddress)

Setzt die aktuelle Basisadresse für den

Zend Engine Disassembler

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  10
bytekit_disassemble_file()

array
bytekit_disassemble_file(string filename[,
array &errors[, mixed options]])

Disassembliert die gewünschte Datei


Erlaubt das Abfangen von Fehlern
und die Übergabe von Optionen

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  11
bytekit_disassemble_file() - Optionen

• „flags“
➡ BYTEKIT_SHOW_SPECIALS

Zeigt „extended_value“ Werte, die als BYTEKIT_EXT_SPECIAL markiert sind

• „dependencies“
• Array von Abhängigkeiten
• Liste der Dateien die zuvor compiliert werden müssen
• Erlaubt Auflösen von Vererbungshierarchien

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  12
Rückgabe von bytekit_disassemble_file()

• FALSE falls ein Fehler aufgetreten ist


• Hauptdatei konnte nicht kompiliert werden
• Abhängigkeit konnte nicht kompiliert werden
• Ansonsten ein prall gefülltes Array
• functions - definierte Funktionen
• classes - definierte Klassen
• base - Basisadresse für nächste Disassemblierung

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  13
Funktionen

'functions' =>
array (
'main' =>
array (
'code' => ..., // disassemblierter Code
'cfg' => ..., // Code Flow Graph
'entries' => ..., // Eingehende Kanten
'exits' => ..., // Ausgehende Kanten
'bb' => ..., // Zuweisung der Basic Blocks
'next_t' => ..., // Wahr - Kanten
'next_f' => ..., // Falsch - Kanten
'exception' => ..., // Exception - Kanten
'raw' => ... // Raw op_array data
),

'TestKlasse::__construct' => ...


)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  14
Funktionen - Disassemblierter Code
'code' =>
array (
0 =>
array (
'address' => ..., // Adresse für Disassembler
'opline' => ..., // zugehörige Opline
'mnemonic' => ..., // Mnemonic - FETCH_CLASS
'operands' =>
array (
0 =>
array (
'string' => ..., // '~4'
'value' => ..., // 4
'type' => ..., // BYTEKIT_TYPE_TMP_VAR
),

1 => ...,
)
)

1 => ...,
)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  15
Funktionen - Code Flow Graph

Enthält die Kanten


'cfg' => zwischen den Basic Blocks
array (
1 =>
array (
3 => BYTEKIT_EDGE_TRUE,
2 => BYTEKIT_EDGE_FALSE,
),

2 =>
array (
3 => BYTEKIT_EDGE_NORMAL
),

3 =>
array (
)
)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  16
Funktionen - Basic Block Zuweisung

'bb' =>
array (
0 => 1,
1 => 1,
2 => 1,
3 => 1,
4 => 1, Weist jede disassemblierte
5 => 2, Codezeile einem Basic Block zu
6 => 2,
7 => 3,
8 => 3,
9 => 3,
10 => 3
)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  17
Funktionen - Kanten

'entries' => 'exits' => 'next_t' => 'next_f' => 'exception' =>
array ( array ( array ( array ( array (
0 => 0, 0 => 1, 0 => 1, 0 => -1, 0 => -1,
1 => 1, 1 => 1, 1 => 2, 1 => -1, 1 => -1,
2 => 1, 2 => 1, 2 => 3, 2 => -1, 2 => -1,
3 => 1, 3 => 1, 3 => 4, 3 => -1, 3 => -1,
4 => 1, 4 => 2, 4 => 7, 4 => 5, 4 => -1,
5 => 1, 5 => 1, 5 => 6, 5 => -1, 5 => -1,
6 => 1, 6 => 1, 6 => 7, 6 => -1, 6 => -1,
7 => 2, 7 => 1, 7 => 8, 7 => -1, 7 => -1,
8 => 1, 8 => 1, 8 => 9, 8 => -1, 8 => -1,
9 => 1, 9 => 1, 9 => 10, 9 => -1, 9 => -1,
10 => 1 10 => 0 10 => -1 10 => -1 10 => -1
) ) ) ) )

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  18
Funktionen - Raw op_array Daten
'raw' =>
array (
'type' => ...,
'function_name' => ...,
'scope' => ...,
'fn_flags' => ...,
'num_args' => ...,
'required_num_args' => ...,
'pass_rest_by_reference' => ...,
'try_catch_array' => ...,
'line_start' => ...,
'line_end' => ...,
'doc_comment'
'return_reference'
=>
=>
...,
...,
Ausgabe der
'refcount'
'last'
=>
=>
...,
...,
op_array Rohdaten
'size'
'T'
=>
=>
...,
...,
wie bei pecl/parsekit
'last_brk_cont' => ...,
'current_brk_cont' => ...,
'backpatch_count' => ...,
'done_pass_two' => ...,
'brk_cont_array' => ...,
'cv' => ...,
'static_variables' => ...,
'start_op' => ...,
'filename' => ...,
'opcodes' => ...
)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  19
Funktionen - Raw opline Daten
'opcodes' =>
array (
0 =>
array (
'opcode' => 99,
'flags' => 132137,
'result' =>
array (
'type' => 2,
'EA.var' => 20,
'EA.type' => 0,
'var' => 1,
'constant' => 20,
),
'op1' => Ausgabe der
array (
'type'
'EA.var'
=> 8,
=> 0,
opline Rohdaten
'EA.type' => 0,
'var' => 0,
wie bei pecl/parsekit
'constant' => 0,
),
'op2' =>
array (
'type' => 1,
'EA.var' => 8080000,
'EA.type' => 5,
'constant' => 'E_ALL',
),
'extended_value' => 0,
'lineno' => 11,
)
)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  20
Klassen
'classes' =>
array (
'TestKlasse' =>
array (
'type' => 2, Ausgabe der
'name' => 'TestKlasse',
'parent' => NULL, class_entry Rohdaten
'constants_updated' => false,
'ce_flags' => 0, wie bei pecl/parsekit
'constructor' => '__construct',
'clone' => NULL,
'__get' => NULL,
'__set' => NULL,
'__call' => NULL,
'properties_info' => NULL,
'static_members' => NULL,
'constants_table' => NULL,
'interfaces' => NULL,
'filename' => '/var/www/bytekit/examples/test.php',
'line_start' => 3,
'line_end' => 9,
'doc_comment' => NULL,
'refcount' => 1,
'methods' =>
array (
0 => '__construct',
),
'default_properties' => NULL,
),
)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  21
Teil III
Bytekit - Beispiele

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  22
Einfacher Disassembler

<?php
$filename = $_SERVER['argv'][1];

if (!file_exists($filename)) {
die("File not found.");
}

$res = bytekit_disassemble_file($filename);

foreach ($res['functions'] as $functionname => &$function) {

echo $functionname, ":\n";


for ($i=0; $i<count($function['code']); $i++) {
$c = &$function['code'][$i];

$operands = "";
for ($j=0; $j<count($c['operands']); $j++) {
$operands .= ($j > 0 ? ", " : "") . $c['operands'][$j]['string'];
}

echo " ", str_pad($c['mnemonic'], 32, " ", STR_PAD_RIGHT), $operands;


echo "\n";
}
echo "\n";
}
?>

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  23
Einfacher Disassembler - Ausgabe
main:
FETCH_R $0, '_SERVER'
FETCH_DIM_R $1, $0, 'argv'
FETCH_DIM_R $2, $1, 1
ASSIGN !0, $2
SEND_VAR !0, 1
DO_FCALL $4, 'file_exists'
BOOL_NOT ~5, $4
JMPZ ~5, loc_40a
EXIT 'File not found.'
JMP loc_40a
SEND_VAR !0, 1
DO_FCALL $6, 'bytekit_disassemble_file'
ASSIGN !1, $6
FETCH_DIM_W $8, !1, 'functions'
FE_RESET $9, $8, loc_444
FE_FETCH $10, ~11, $9, loc_444
ASSIGN_REF !3, $10, 0
ASSIGN !2, ~11
ECHO !2
ECHO ':\n'
ASSIGN !4, 0
FETCH_DIM_R $14, !3, 'code'
SEND_VAR $14, 1
DO_FCALL $15, 'count'
IS_SMALLER ~16, !4, $15
JMPZNZ ~16, loc_442, loc_41e
POST_INC ~17, !4
FREE ~17
JMP loc_416
...

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  24
Disassembler - Sammeln der Sprunglabels

<?php
...
$jumptargets = array();
for ($i=0; $i<count($function['code']); $i++) {
$c = &$function['code'][$i];
potentielles
for ($j=0; $j<count($c['operands']); $j++) { Sprungziel
$op = &$c['operands'][$j];

if ($op['type'] == BYTEKIT_TYPE_SYMBOL) {
$jumptargets[$op['value']] = $op['string'];
}
}
}

echo $functionname, ":\n";


for ($i=0; $i<count($function['code']); $i++) { Ausgabe
$c = &$function['code'][$i]; des
Labels
if (isset($jumptargets[$c['address']])) {
echo $jumptargets[$c['address']],":\n";
}
...
?>

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  25
Disassembler - Ausgabe mit Sprunglabels
main:
FETCH_R $0, '_SERVER'
FETCH_DIM_R $1, $0, 'argv'
FETCH_DIM_R $2, $1, 1
ASSIGN !0, $2
SEND_VAR !0, 1
DO_FCALL $4, 'file_exists'
BOOL_NOT ~5, $4
JMPZ ~5, loc_40a
EXIT 'File not found.'
JMP loc_40a
loc_40a:
SEND_VAR !0, 1
DO_FCALL $6, 'bytekit_disassemble_file'
ASSIGN !1, $6
FETCH_DIM_W $8, !1, 'functions'
FE_RESET $9, $8, loc_444
FE_FETCH $10, ~11, $9, loc_444
loc_40f:
ASSIGN_REF !3, $10, 0
ASSIGN !2, ~11
ECHO !2
ECHO ':\n'
ASSIGN !4, 0
loc_416:
FETCH_DIM_R $14, !3, 'code'
SEND_VAR $14, 1
DO_FCALL $15, 'count'
IS_SMALLER ~16, !4, $15
JMPZNZ ~16, loc_442, loc_41e
loc_41b:
POST_INC ~17, !4
FREE ~17
JMP loc_416

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  26
Visualisierung des Bytecodes (I)

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  27
Visualisierung des Bytecodes (II)

• Export in eine .dot Datei zur Visualisierung mit Graphviz


digraph classgraph {
node [
fontname="Courier"
fontsize="12"
shape="plaintext"
];
graph [
rankdir="HR"
bgcolor="#f7f7f7"
label="Control Flow Graph for main()"
labeljust="c"
labelloc="t"
fontname="Courier"
fontsize="16"
];
mindist = 0.4;
overlap = false;

"bb_1" [...]; "bb_2" [...]; "bb_3" [...]; ...

"bb_1" -> "bb_2" [...]; ...


}

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  28
Visualisierung des Bytecodes (III)
• Basic Blocks sind als HTML Nodes definiert
"bb_1" [
label =<<TABLE BORDER="2" CELLBORDER="0" CELLSPACING="0" BGCOLOR="#ffffff">
<TR>
<TD BGCOLOR="#FF8888" colspan="3" ALIGN="LEFT">
<FONT face="Courier-Bold" point-size="12">00000400:</FONT></TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000400</TD>
<TD ALIGN="LEFT">FETCH_R</TD>
<TD ALIGN="LEFT">$0, '_SERVER'</TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000401</TD>
<TD ALIGN="LEFT">FETCH_DIM_R</TD>
<TD ALIGN="LEFT">$1, $0, 'argv'</TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000402</TD>
<TD ALIGN="LEFT">FETCH_DIM_R</TD>
<TD ALIGN="LEFT">$2, $1, 1</TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000403</TD>
<TD ALIGN="LEFT">ASSIGN</TD>
<TD ALIGN="LEFT">!0, $2</TD>
</TR>
...
</TABLE>>
];

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  29
Visualisierung - Aufbau der Nodes

$code = &$function['code'];
$bb = &$function['bb'];
$bbid = -1;
for ($i=0; $i<count($code); $i++) {
$instruction = &$code[$i];

/* check for new basic block */


if ($bbid != $bb[$i]) {
$bbid = $bb[$i];
$nodes[$bbid] = array();
$nodes[$bbid]['address'] = $instruction['address'];
$nodes[$bbid]['instructions'] = array();
$nodes[$bbid]['id'] = $cnt++;
}
$instr = array();
$instr['address'] = $instruction['address'];
$instr['mnemonic'] = $instruction['mnemonic'];
$instr['operands'] = '';
for ($j = 0; $j<count($instruction['operands']); $j++) {
$instr['operands'] .= ($j != 0) ? ', ' : '';
$instr['operands'] .= $instruction['operands'][$j]['string'];
}
$nodes[$bbid]['instructions'][] = $instr;

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  30
Visualisierung - Ausgabe der Nodes

foreach ($nodes as $key => $value) {


$bgcolor = "#FF8888";

fprintf($fout, '"bb_%d" [', $key);


fprintf($fout, "\nlabel =<<TABLE BORDER=\"2\" CELLBORDER=\"0\" CELLSPACING=\"0\" ".
"BGCOLOR=\"#ffffff\">\n");
fprintf($fout, "<TR><TD BGCOLOR=\"$bgcolor\" colspan=\"3\" ALIGN=\"LEFT\">".
"<FONT face=\"Courier-Bold\" ");
fprintf($fout, "point-size=\"12\">%s:</FONT></TD></TR>\n",
sprintf("%08x",$nodes[$key]['address']));

foreach($nodes[$key]['instructions'] as $instr) {
fprintf($fout, "<TR><TD ALIGN=\"LEFT\">%s</TD><TD ALIGN=\"LEFT\">%s</TD> ".
"<TD ALIGN=\"LEFT\">%s</TD></TR>\n",
htmlentities(sprintf("%08x",$instr['address'])),
htmlentities($instr['mnemonic']),
htmlentities($instr['operands']));
}

fprintf($fout, "</TABLE>>\n");
fprintf($fout, "];\n");
}

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  31
Visualisierung - Ausgabe der Kanten

$cfg = &$function['cfg'];

for ($i = 1; $i <= count($cfg); $i++) {


if (count($cfg[$i]) == 0) continue;
foreach ($cfg[$i] as $key => $value) {
if ($nodes[$i] == false) continue;

switch ($value) {
case BYTEKIT_EDGE_TRUE:
$style = 'color="#00ff00"';
break;
case BYTEKIT_EDGE_FALSE:
$style = 'color="#ff0000"';
break;
case BYTEKIT_EDGE_NORMAL:
$style = 'color="#000000"';
break;
case BYTEKIT_EDGE_EXCEPTION:
$style = 'style=dotted, penwidth=3.0, color="#0000ff"';
break;
default:
$style = 'color="#0000ff"';
}
fprintf($fout, '"bb_%d" -> "bb_%d" [%s];'."\n", $i, $key, $style);
}
}

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  32
Suche nach eval() - Traditionell

• z.B. für ein Verbot von eval() im pre-commit-hook

• Traditionell Suche per grep -R “eval(“ *


• Sehr ungenau mit vielen „false positives“
• Schwer automatisch auszuwerten

wp-admin/import/blogger.php: eval('this.status='+text);
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCL...
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PC...
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'...
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT]....
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].....
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT]....
wp-admin/press-this.php: var my_src = eval(
wp-admin/press-this.php: var my_src = eval(
wp-includes/classes.php: eval("@\$query = \"" . addslashes($query) . "\";");
wp-includes/functions.php: if ( doubleval($bytes) >= $mag )
wp-includes/js/jquery/interface.js:eval(function(p,a,c,k,e,r){e=functi...
wp-includes/js/prototype.js: return this.extractScripts().map(function(script){return eval(script)});
wp-includes/js/prototype.js: if (!sanitize || json.isJSON()) return eval('(' + json + ')');
...
wp-includes/js/tinymce/tiny_mce_popup.js: eval(s);
wp-includes/js/tw-sack.js: eval(this.response);
wp-includes/rewrite.php: eval("\$query = \"" . addslashes($query) . "\";");

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  33
Suche nach eval() im Bytecode

• Sucht durch alle Funktionen <?php


$fname = $_SERVER['argv'][1];

• Sucht nach EVAL mnemonic $res = bytekit_disassemble_file($fname);

foreach ($res['functions'] as $key => &$func) {


• keine „false positives“ $c = &$func['code'];
for ($i=0; $i<count($c); $i++) {
• lässt sich automatisch if ($c[$i]['mnemonic'] == "EVAL") {
die("found\n");
auswerten - z.B. im pre- }
commit-hook }
}

• Zeilennummer lässt sich aus die("not found\n");


der opline -> lineno ableiten ?>

wp-admin/includes/class-pclzip.php(2576): eval('$v_result = '.$p_options[PCLZI...


wp-admin/includes/class-pclzip.php(2714): eval('$v_result = '.$p_options[PCLZI...
wp-admin/includes/class-pclzip.php(3605): eval('$v_result = '.$p_options[PCLZI...
wp-admin/includes/class-pclzip.php(3879): eval('$v_result = '.$p_options[PCLZI...
wp-admin/includes/class-pclzip.php(3929): eval('$v_result = '.$p_options[PCLZI...
wp-admin/includes/class-pclzip.php(4005): eval('$v_result = '.$p_options[PCLZI...
wp-includes/classes.php(219): eval("@\$query = \"" . addslashes($query) . "\";");
wp-includes/rewrite.php(299): eval("\$query = \"" . addslashes($query) . "\";");

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  34
Einfacher Include/Require/Eval Scanner (I)

• Suche nach „manipulierbare“ Includes durch Erkennen


von „nicht-manipulierbare“ Includes

• Konstante Includes sind nicht manipulierbar


• op1.type == BYTEKIT_IS_CONST

• beschränkte Datenflussanalyse
• suche nur im gleichen Basic Block
• Emulation von einigen wenigen Opcodes
• bei Unbekannten Opcodes wird „Manipulierbarkeit“ angenommen

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  35
Einfacher Include/Require/Eval Scanner (II)

• Emuliert wird
• INCLUDE_OR_EVAL
• ASSIGN
• ASSIGN_CONCAT
• CONCAT
• FETCH_CONSTANT

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  36
Suche nach Include/Require/Eval im Bytecode

/* Search in all functions */


$found = array();
foreach ($res['functions'] as $name => &$func) {
$r = &$func['raw'];
$c = &$func['code'];
$o = &$r['opcodes'];

/* Find all INCLUDE_OR_EVAL opcodes */


for ($i=0; $i<count($o); $i++) {
if ($o[$i]['opcode'] == BYTEKIT_INCLUDE_OR_EVAL) {

/* Find the first opline of this BB */


$end = $start = $i;
$start_bb = bytekit_opline_to_bb($func, $i);
while ($end > 0) {
if ($start_bb != bytekit_opline_to_bb($func, $end)) break;
$end--;
}

$is_safe = bytekit_trace_backward($func, $start-1, $end, $o[$i]['op1']);

if ($is_safe == BYTEKIT_IS_UNSAFE) {
$found[] = $o[$i]['lineno'];
}
}
}
}

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  37
Rückwärts Datenfluss Analyse (I)
<?php
function bytekit_trace_backward(&$func, $start, $end, &$s)
{
$o = &$func['raw']['opcodes'];

if ($s['type'] == BYTEKIT_IS_CONST) {
return BYTEKIT_IS_SAFE;
}

$i = $start;
while ($i >= $end)
{
$ol = &$o[$i];
$opcode = $ol['opcode'];

switch ($opcode) {

/*******************/
/* EMULATE OPCODES */
/*******************/

default: return BYTEKIT_IS_UNSAFE;


}
$i--;
}

return BYTEKIT_IS_UNSAFE;
}
?>

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  38
Rückwärts Datenfluss Analyse (II)
case BYTEKIT_INCLUDE_OR_EVAL:
if (bytekit_equal_node($s, $ol['result'])) {
return BYTEKIT_IS_UNSAFE;
}
break;
case BYTEKIT_FETCH_CONSTANT:
if (bytekit_equal_node($s, $ol['result'])) {
return BYTEKIT_IS_SAFE;
}
break;
case BYTEKIT_ASSIGN:
if (bytekit_equal_node($s, $ol['result']) || bytekit_equal_node($s, $ol['op1'])) {
return bytekit_trace_backward($func, $i-1, $end, $ol['op2']);
}
break;
case BYTEKIT_ASSIGN_CONCAT:
if (bytekit_equal_node($s, $ol['op1'])) {
$res1 = bytekit_trace_backward($func, $i-1, $end, $ol['op1']) == BYTEKIT_IS_SAFE;
$res2 = bytekit_trace_backward($func, $i-1, $end, $ol['op2']) == BYTEKIT_IS_SAFE;
return ($res1 && $res2) ? BYTEKIT_IS_SAFE : BYTEKIT_IS_UNSAFE;
}
break;
case BYTEKIT_CONCAT:
if (bytekit_equal_node($s, $ol['result'])) {
$res1 = bytekit_trace_backward($func, $i-1, $end, $ol['op1']) == BYTEKIT_IS_SAFE;
$res2 = bytekit_trace_backward($func, $i-1, $end, $ol['op2']) == BYTEKIT_IS_SAFE;
return ($res1 && $res2) ? BYTEKIT_IS_SAFE : BYTEKIT_IS_UNSAFE;
}
break;

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  39
Include Checker in Aktion

<?php
define("MODULE", "module1");
define("BASE_PATH", "/directory/");

include "/konstante.php";
include BASE_PATH . MODULE . ".php";
include $x . ".php";
$x = MODULE . "1.";
$y = $x;
include $y . ".php";
?>

$ php -d extension=bytekit.so check_include.php includes.php


includes.php(7): include $x . ".php";

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  40
Danke fürs Zuhören...

Fragen ?
http://www.bytekit.org

Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 •  41

Das könnte Ihnen auch gefallen