Sie sind auf Seite 1von 41
http://www.sektioneins.de
http://www.sektioneins.de

Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode Stefan Esser < stefan.esser@sektioneins.de > Mai 2009

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

Entwicklung bei SektionEins GmbH Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Teil I

Einführung

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

3

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

Einführung

PHP Bytecode

ist die compilierte Form von PHP

gibt guten Einblick in Funktionsweise von PHP

erlaubt Analysen auf der Ausführungsebene

rückt mehr und mehr in den Blickpunkt

Optimizer - Static Analysis - Dynamic Analysis

Existierende Tools für uns unzureichend

vld - Vulcan Logic Disassembler

parsekit - Zugriff auf Bytecode Rohdaten

PHP Skript
PHP Skript
• parsekit - Zugriff auf Bytecode Rohdaten PHP Skript PHP Quelltext Compiler PHP Bytecode Executor Stefan

PHP

Quelltext

Compiler
Compiler
auf Bytecode Rohdaten PHP Skript PHP Quelltext Compiler PHP Bytecode Executor Stefan Esser • Bytekit -

PHP

Bytecode

Executor
Executor

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

4

Compiler PHP Bytecode Executor Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

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

• hilft bei Visualisierung Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Teil II

Bytekit - API

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

6

Teil II Bytekit - API Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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

&errors[, mixed options]] ) Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

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

Opcodes - BYTEKIT_INCLUDE_OR_EVAL Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode •

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

für den Zend Engine Disassembler Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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

für den Zend Engine Disassembler Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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

und die Übergabe von Optionen Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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

von Vererbungshierarchien Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai

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

für nächste Disassemblierung Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode •

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' =>

=>

'raw'

, // Exception - Kanten // Raw op_array data

'TestKlasse::

construct'

=>

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

14

construct' => Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai

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

) 1 ) => , ) 1 => ), , Stefan Esser • Bytekit - Ein

Funktionen - Code Flow Graph

'cfg' => array (

1 =>

Enthält die Kanten zwischen den Basic Blocks

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

) ), 3 => array ( ) Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit

Funktionen - Basic Block Zuweisung

'bb' => array (

0 => 1,

1 => 1,

2 => 1,

3 => 1,

4 => 1,

5 => 2,

6 => 2,

7 => 3,

8 => 3,

9 => 3,

10 => 3

)

Weist jede disassemblierte Codezeile einem Basic Block zu

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

17

Codezeile einem Basic Block zu Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

Funktionen - Kanten

'entries' => array (

0 => 0,

1 => 1,

2 => 1,

3 => 1,

4 => 1,

5 => 1,

6 => 1,

7 => 2,

8 => 1,

9 => 1,

10 => 1

)

'exits' => array (

0 => 1,

1 => 1,

2 => 1,

3 => 1,

4 => 2,

5 => 1,

6 => 1,

7 => 1,

8 => 1,

9 => 1,

10 => 0

)

'next_t' => array (

0 => 1,

1 => 2,

2 => 3,

3 => 4,

4 => 7,

5 => 6,

6 => 7,

7 => 8,

8 => 9,

9 => 10,

10 => -1

)

'next_f' => array (

0 => -1,

1 => -1,

2 => -1,

3 => -1,

4 => 5,

5 => -1,

6 => -1,

7 => -1,

8 => -1,

9 => -1,

10 => -1

)

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

'exception' => array (

18

0 => -1,

1 => -1,

2 => -1,

3 => -1,

4 => -1,

5 => -1,

6 => -1,

7 => -1,

8 => -1,

9 => -1,

10 => -1

)

-1, 2 => -1, 3 => -1, 4 => -1, 5 => -1, 6 => -1,

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'

=>

,

'refcount'

=>

,

'last'

=>

,

'size'

=>

,

'T'

=>

,

'last_brk_cont'

=>

,

'current_brk_cont'

=>

,

'backpatch_count'

=>

,

'done_pass_two'

=>

,

'brk_cont_array'

=>

,

'cv'

=>

,

'static_variables'

=>

,

'start_op'

=>

,

'filename'

=>

,

'opcodes'

=>

)

Ausgabe der op_array Rohdaten wie bei pecl/parsekit

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

19

Rohdaten wie bei pecl/parsekit Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Funktionen - Raw opline Daten

'opcodes' => array ( 0 =>

array ( 'opcode' => 99,

'flags' => 132137, 'result' => array (

'type'

=> 2,

'EA.var'

=> 20,

'EA.type' => 0,

'var'

'constant' => 20,

=> 1,

), 'op1' => array ( 'type' 'EA.var'

), 'op1' => array ( 'type' 'EA.var'

=> 8,

=> 0,

)

)

'EA.type' => 0,

'var'

'constant' => 0,

=> 0,

), 'op2' => array ( 'type' 'EA.var'

=> 1, => 8080000,

'EA.type' => 5, 'constant' => 'E_ALL',

),

'extended_value' => 0,

'lineno'

=> 11,

Ausgabe der opline Rohdaten wie bei pecl/parsekit

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

20

Rohdaten wie bei pecl/parsekit Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Klassen

'classes' => array ( 'TestKlasse' => array ( 'type' => 2, 'name' => 'TestKlasse', 'parent' => NULL, 'constants_updated' => false, 'ce_flags' => 0,

Ausgabe der class_entry Rohdaten wie bei pecl/parsekit

'constructor' => ' construct', 'clone' => NULL,

get' '

set' '

call' '

'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 (

=> NULL,

=> NULL,

=> NULL,

0 => '

construct',

), 'default_properties' => NULL,

),

)

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

21

=> NULL, ), ) Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Teil III

Bytekit - Beispiele

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

22

Teil III Bytekit - Beispiele Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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 " echo "\n";

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

}

echo "\n";

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

23

; } echo " \n " ; Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit

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

FREE ~17 JMP loc_416 Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Disassembler - Sammeln der Sprunglabels

<?php

?>

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

for ($j=0; $j<count($c['operands']); $j++) { $op = &$c['operands'][$j];

$j ++ ) { $op = & $c [ 'operands' ][ $j ]; potentielles Sprungziel if

potentielles

Sprungziel

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

}

}

}

echo $functionname, ":\n"; for ($i=0; $i<count($function['code']); $i++) { $c = &$function['code'][$i];

if (isset($jumptargets[$c['address']])) { echo $jumptargets[$c['address']],":\n";

}

Ausgabe des Labels
Ausgabe
des
Labels

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

25

\n " ; } Ausgabe des Labels Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit

Disassembler - Ausgabe mit Sprunglabels

main:

FETCH_R FETCH_DIM_R FETCH_DIM_R ASSIGN SEND_VAR DO_FCALL BOOL_NOT JMPZ EXIT JMP

loc_40a:

SEND_VAR DO_FCALL ASSIGN FETCH_DIM_W FE_RESET FE_FETCH

loc_40f:

$0, '_SERVER' $1, $0, 'argv' $2, $1, 1 !0, $2 !0, 1 $4, 'file_exists' ~5, $4 ~5, loc_40a 'File not found.'

loc_40a

!0, 1 $6, 'bytekit_disassemble_file' !1, $6 $8, !1, 'functions' $9, $8, loc_444 $10, ~11, $9, loc_444

ASSIGN_REF ASSIGN

!3, $10, 0 !2, ~11

ECHO

!2

ECHO

':\n'

ASSIGN

!4, 0

loc_416:

FETCH_DIM_R SEND_VAR DO_FCALL IS_SMALLER JMPZNZ

loc_41b:

$14, !3, 'code' $14, 1 $15, 'count' ~16, !4, $15 ~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 •

26

FREE ~17 JMP loc_416 Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Visualisierung des Bytecodes (I)

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

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

27

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

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_1" -> "bb_2" [

];

];

"bb_3" [

];

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

28

[ ]; ]; "bb_3" [ ]; Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit

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>

ALIGN="LEFT"> !0, $2 </TD> </TR> </TABLE>> ]; Stefan Esser • Bytekit - Ein

</TABLE>>

];

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

29

</TABLE>> ]; Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai

Visualisierung - Aufbau der Nodes

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

$bb

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

= &$function['bb'];

/* 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

][] = $instr ; } Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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

( $fout , "]; \n " ); Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit

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

, $i , $key , $style ); } } Stefan Esser • Bytekit - Ein Werkzeug

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:

wp-admin/includes/class-pclzip.php:

wp-admin/includes/class-pclzip.php:

wp-admin/includes/class-pclzip.php:

wp-admin/includes/class-pclzip.php:

wp-admin/includes/class-pclzip.php:

wp-admin/includes/class-pclzip.php:

wp-admin/press-this.php:

wp-admin/press-this.php:

wp-includes/classes.php:

wp-includes/functions.php:

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:

eval('this.status='+text); eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCL eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PC eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].' eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT] eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT] eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT] var my_src = eval( var my_src = eval( eval("@\$query = \"" . addslashes($query) . "\";"); if ( doubleval($bytes) >= $mag )

if (!sanitize || json.isJSON()) return eval('(' + json + ')');

wp-includes/js/tinymce/tiny_mce_popup.js:

wp-includes/js/tw-sack.js:

wp-includes/rewrite.php:

eval(s);

eval(this.response); eval("\$query = \"" . addslashes($query) . "\";");

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

33

. "\";"); Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai

Suche nach eval() im Bytecode

Sucht durch alle Funktionen

Sucht nach EVAL mnemonic

keine „false positives“

lässt sich automatisch auswerten - z.B. im pre- commit-hook

Zeilennummer lässt sich aus der opline -> lineno ableiten

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

$res = bytekit_disassemble_file($fname);

foreach ($res['functions'] as $key => &$func) { $c = &$func['code']; for ($i=0; $i<count($c); $i++) { if ($c[$i]['mnemonic'] == "EVAL") { die("found\n");

}

}

}

die("not found\n");

?>

wp-admin/includes/class-pclzip.php(2576): eval('$v_result = '.$p_options[PCLZI
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

. "\";"); Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai

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

„Manipulierbarkeit“ angenommen Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai

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

CONCAT • FETCH_CONSTANT Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode •

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

$o [ $i ][ 'lineno' ]; } } Stefan Esser • Bytekit - Ein Werkzeug zur

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

-- ; return BYTEKIT_IS_UNSAFE ; Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP

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

: BYTEKIT_IS_UNSAFE ; } break ; Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit

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";
$ 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

include $x . ".php"; Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode

Danke fürs Zuhören

Fragen ?

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

41

Fragen ? http://www.bytekit.org Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode •