Zákeřný eval()

870d613430249e453343efc9667ef636?s=47 Petr Viktorin
September 27, 2012

Zákeřný eval()

Zrádnosti funkce eval(), a co použít místo ní. Přednáška pro zářijové PyVo 2012.

870d613430249e453343efc9667ef636?s=128

Petr Viktorin

September 27, 2012
Tweet

Transcript

  1. Zákeřný eval() Petr Viktorin encukou@gmail.com nebezpečné PyVo 2012-09-27

  2. $ pydoc eval eval(source[, globals[, locals]]) Evaluate the source in

    the context of globals and locals. The source may be a string representing a Python expression or a code object as returned by compile().
  3. import os import math f = raw_input ( " Zadej

    funkci : " ) for x in range ( 5 ) : print " x = %s , y = %s " % ( x , eval ( f ) )
  4. Zadej funkci: x * 5 x = 0, y =

    0 x = 1, y = 5 x = 2, y = 10 x = 3, y = 15 x = 4, y = 20
  5. Zadej funkci: x * 5 x = 0, y =

    0 x = 1, y = 5 x = 2, y = 10 x = 3, y = 15 x = 4, y = 20 Zadej funkci: math.sin(x) x = 0, y = 0.0 x = 1, y = 0.841470984808 x = 2, y = 0.909297426826 x = 3, y = 0.14112000806 x = 4, y = -0.756802495308
  6. Zadej funkci: x * 5 x = 0, y =

    0 x = 1, y = 5 x = 2, y = 10 x = 3, y = 15 x = 4, y = 20 Zadej funkci: math.sin(x) x = 0, y = 0.0 x = 1, y = 0.841470984808 x = 2, y = 0.909297426826 x = 3, y = 0.14112000806 x = 4, y = -0.756802495308 Zadej funkci: x ** 999999 x = 0, y = 0 x = 1, y = 1
  7. Zadej funkci: x * 5 x = 0, y =

    0 x = 1, y = 5 x = 2, y = 10 x = 3, y = 15 x = 4, y = 20 Zadej funkci: math.sin(x) x = 0, y = 0.0 x = 1, y = 0.841470984808 x = 2, y = 0.909297426826 x = 3, y = 0.14112000806 x = 4, y = -0.756802495308 Zadej funkci: x ** 999999 x = 0, y = 0 x = 1, y = 1 Zadej funkci: open('/etc/passwd').read() x = 0, y = root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin
  8. Zadej funkci: x * 5 x = 0, y =

    0 x = 1, y = 5 x = 2, y = 10 x = 3, y = 15 x = 4, y = 20 Zadej funkci: math.sin(x) x = 0, y = 0.0 x = 1, y = 0.841470984808 x = 2, y = 0.909297426826 x = 3, y = 0.14112000806 x = 4, y = -0.756802495308 Zadej funkci: x ** 999999 x = 0, y = 0 x = 1, y = 1 Zadej funkci: open('/etc/passwd').read() x = 0, y = root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin Zadej funkci: os.system('fire-mah-lazors --target=alderaan')
  9. Jak se bránit?

  10. $ pydoc eval eval(source[, globals[, locals]]) The globals must be

    a dictionary and locals can be any mapping, defaulting to the current globals and locals. If only globals is given, locals defaults to it.
  11. >>> import math >>> eval ( "math . sin (

    3 . 1 4 1 ) " ) 0.0005926535550994539
  12. >>> import math >>> eval ( "math . sin (

    3 . 1 4 1 ) " ) 0.0005926535550994539 >>> eval ( "math . sin ( 3 . 1 4 1 ) " , { } ) Traceback ( most recent c a l l l a s t ) : . . . NameError : name 'math' is not defined
  13. >>> import math >>> import os >>> eval ( "math

    . sin ( 3 . 1 4 1 ) " , dict ( math=math) ) 0.0005926535550994539
  14. >>> import math >>> import os >>> eval ( "math

    . sin ( 3 . 1 4 1 ) " , dict ( math=math) ) 0.0005926535550994539 >>> eval ( " os . system ( ' echo pwned ' ) " , dict (math=math) ) Traceback ( most recent c a l l l a s t ) : . . . NameError : name 'os' is not defined
  15. Už je to bezpečné?

  16. >>> import math >>> import os >>> eval ( "

    dir ( ) " ) [ ' __builtins__ ' , '__doc__ ' , '__name__ ' , ' __package__ ' , 'math ' , ' os ' ]
  17. >>> import math >>> import os >>> eval ( "

    dir ( ) " ) [ ' __builtins__ ' , '__doc__ ' , '__name__ ' , ' __package__ ' , 'math ' , ' os ' ] >>> eval ( " dir ( ) " , dict (math=math) ) [ ' __builtins__ ' , 'math ' ]
  18. >>> dir ( __builtins__ ) [ . . . ,

    ' abs ' , ' a l l ' , ' any ' , ' apply ' , ' basestring ' , ' bin ' , ' bool ' , ' buffer ' , ' bytearray ' , ' bytes ' , ' callable ' , ' chr ' , 'cmp ' , . . . , ' vars ' , ' xrange ' , ' zip ' ]
  19. >>> dir ( __builtins__ ) [ . . . ,

    ' abs ' , ' a l l ' , ' any ' , ' apply ' , ' basestring ' , ' bin ' , ' bool ' , ' buffer ' , ' bytearray ' , ' bytes ' , ' callable ' , ' chr ' , 'cmp ' , . . . , ' vars ' , ' xrange ' , ' zip ' ] >>> eval ( ' ' ' open ( ' / etc / passwd ' ) . read ( ) ' ' ' , { } ) 'root:x:0:0:root:/root:/bin/bash\n ...
  20. >>> eval ( "open ( ' / etc / passwd

    ' ) . read ( ) " , dict ( __builtins__ = { } ) ) Traceback ( most recent c a l l l a s t ) : . . . NameError : name 'open' is not defined
  21. >>> eval ( "open ( ' / etc / passwd

    ' ) . read ( ) " , dict ( __builtins__ = { } ) ) Traceback ( most recent c a l l l a s t ) : . . . NameError : name 'open' is not defined >>> eval ( " abs(−5) " , dict ( __builtins__ = { } , abs=abs ) ) 5
  22. Už je to bezpečné!?

  23. eval() vyhodnotí jakýkoli výraz

  24. Co není výraz? def f ( x ) : return

    3 + x
  25. Co není výraz? def f ( x ) : return

    3 + x for x in range (10) : print x
  26. Co není výraz? def f ( x ) : return

    3 + x for x in range (10) : print x x = 3
  27. Co není výraz? def f ( x ) : return

    3 + x for x in range (10) : print x x = 3 import os a další příkazy: http://docs.python.org/reference/index.html
  28. Jak to obejít?

  29. def f ( x ) : return 3 + x

  30. def f ( x ) : return 3 + x

    f = ( lambda x : 3 + x ) !
  31. x = 3 * 8 foobar ( x )

  32. x = 3 * 8 foobar ( x ) (

    lambda x=3 ** 8: foobar ( x ) ) ( )
  33. for x in range (10) : print x

  34. for x in range (10) : print x [ sys

    . stdout . write ( '%s \ n ' % x ) for x in range (10) ]
  35. def f ( x ) : return 3 + x

    for i in range (10) : print f ( i )
  36. def f ( x ) : return 3 + x

    for i in range (10) : print f ( i ) ( lambda f =( lambda x : 3 + x ) , out=sys . stdout . write : [ out ( '%s \ n ' % f ( x ) ) for x in range (10) ] ) ( ) !
  37. class T( object ) : def __init__ ( self ,

    p) : s e l f . p = p
  38. class T( object ) : def __init__ ( self ,

    p) : s e l f . p = p T = type ( "T" , ( object , ) , { ' __init__ ' : lambda self , p : setattr ( self , 'p ' , p) } )
  39. import os os . system ( " echo pwned" )

  40. import os os . system ( " echo pwned" )

    __import__ ( ' os ' ) . system ( " echo pwned" ) !
  41. import os os . system ( " echo pwned" )

    ( lambda os=__import__ ( ' os ' ) : os . system ( " echo pwned" ) ) ( )
  42. Téměř jakýkoli program se dá převést na jediný výraz

  43. Téměř jakýkoli program se dá převést na jediný výraz eval()

    vyhodnotí jakýkoli výraz
  44. Téměř jakýkoli program se dá převést na jediný výraz eval()

    vyhodnotí jakýkoli výraz Ještě tak mít ten __import__...
  45. >>> (42) . bit_length ( ) 6

  46. >>> (42) . bit_length ( ) 6 >>> dir (42)

    [ . . . , '__add__ ' , ' __class__ ' , '__doc__ ' , ' __hash__ ' , ' __str__ ' , ' bit_length ' , . . . ]
  47. >>> (42) . bit_length ( ) 6 >>> dir (42)

    [ . . . , '__add__ ' , ' __class__ ' , '__doc__ ' , ' __hash__ ' , ' __str__ ' , ' bit_length ' , . . . ] >>> (42) . __class__ <type ' int ' >
  48. >>> (42) . __class__ . mro ( ) [ <

    type ' int ' > , <type ' object ' >]
  49. >>> (42) . __class__ . mro ( ) [ <

    type ' int ' > , <type ' object ' >] >>> ' 42 ' . __class__ . mro ( ) [ < type ' str ' > , <type ' basestring ' > , <type ' object ' >]
  50. >>> (42) . __class__ . mro ( ) [ <

    type ' int ' > , <type ' object ' >] >>> ' 42 ' . __class__ . mro ( ) [ < type ' str ' > , <type ' basestring ' > , <type ' object ' >] >>> (42) . __class__ . mro ( ) [ −1] <type ' object ' >
  51. >>> (42) . __class__ . mro ( ) [ −1]

    <type 'object'>
  52. >>> (42) . __class__ . mro ( ) [ −1]

    <type 'object'> >>> ( ( 4 2 ) . __class__ . mro ( ) [ −1]. __subclasses__ ( ) ) [<type 'type'>, <type 'weakref'>, ..., <type 'int'>, <type 'basestring'>, ..., <type 'super'>, <type 'xrange'>, ..., <type 'function'>, <type 'classobj'>, ..., <type 'sys.version_info'>, ..., <class 'warnings.catch_warnings'>, ..., <class 'codecs.IncrementalEncoder'>, ...]
  53. >>> [ c for c in ( ( 4 2

    ) . __class__ . mro ( ) [ −1]. __subclasses__ ( ) ) i f c . __name__ == ' f i l e ' ] [ 0 ] <type 'file'>
  54. >>> [ c for c in (42) . __class__ .

    mro ( ) [ −1]. __subclasses__ ( ) i f c . __name__ == ' f i l e ' ] [ 0 ] ( ' / etc / passwd ' ) . read ( ) 'root:x:0:0:root:/root:/bin/bash\n . . .
  55. ( lambda fc =( lambda n : [ c for

    c in ( ) . __class__ . __bases__ [ 0 ] . __subclasses__ ( ) i f c . __name__ == n ] [ 0 ] ) : fc ( " function " ) ( fc ( " code " ) ( 0 ,0 ,0 ,0 , "KABOOM" , ( ) , ( ) , ( ) , " " , " " ,0 , " " ) , { } ) ( ) ) ( )
  56. Ale eval()ovaný kód se dá předem vyčistit!!!!

  57. Ale eval()ovaný kód se dá předem vyčistit!!!! Znáte problémy s

    os.system? SQL injection? XSS?
  58. Ale eval()ovaný kód se dá předem vyčistit!!!! Znáte problémy s

    os.system? SQL injection? XSS? Znáte moduly rexec a bastion?
  59. Ale eval()ovaný kód se dá předem vyčistit!!!! Znáte problémy s

    os.system? SQL injection? XSS? Znáte moduly rexec a bastion? ... nedá!!!!
  60. Co místo evalu?

  61. Věřím svým uživatelům! Opravdový modul

  62. Věřím svým uživatelům! Opravdový modul Dělám kalkulačku! pyparsing, PLY

  63. Věřím svým uživatelům! Opravdový modul Dělám kalkulačku! pyparsing, PLY Chci

    načíst dict/list! ast.literal_eval
  64. Věřím svým uživatelům! Opravdový modul Dělám kalkulačku! pyparsing, PLY Chci

    načíst dict/list! ast.literal_eval Čtu/píšu data/koniguraci! YAML, JSON, ConfigParser
  65. Věřím svým uživatelům! Opravdový modul Dělám kalkulačku! pyparsing, PLY Chci

    načíst dict/list! ast.literal_eval Čtu/píšu data/koniguraci! YAML, JSON, ConfigParser Dělám překladač/REPL! compile & exec()
  66. Pozor na to, že eval se zákeřně schovává ve funkci

    input! input(x) ⇔ eval(raw_input(x))
  67. ? Petr Viktorin @encukou encukou@gmail.com Slajdy jsou pod licencí Creative

    Commons Attribution-ShareAlike 3.0 http://creativecommons.org/licenses/by-sa/3.0/
  68. Zdroje (pozor, většinou zastaralé) http://nedbatchelder.com/blog/201206/eval_really_is_ dangerous.html http://lybniz2.sourceforge.net/safeeval.html http://effbot.org/zone/librarybook-core-eval.htm