Can you program your way past airport security? The usual rules apply.
No liquids are allowed in your hand luggage:
Can you, as a thirsty passenger, take some water with you anyways? Leave your answer in a comment. The comments will stay private for a while, and be published later in the next blog post.. Try to solve it, and then look at the solutions in the comments below!
Update: comments were briefly not moderated, already showing the solution immediately; sorry for that.
Update: Let's try to do it differently from the other puzzles then! Only look at the comments below after you're tried to solve it yourself!
This one is not so hard. The trick is that the ArrayList constructor calls toArray() on the original collection and uses that result as a backing array. With some simple code, we can get hold of that array and modify it after the security check.
Praveen wrote on 2012-03-16 14:11
Good explanation!
Thomas wrote on 2012-03-08 14:26
All you need is a class implementing the Collection interface whose return value of toArray() is under your control. For shortness I used ArrayList as a base class instead of implementing all the methods of the Collection interface myself.
Marc wrote on 2012-03-08 14:43
I did almost the same as Ives, but by creating a Collection that replaces instead of adds.
Sean Reilly wrote on 2012-03-08 14:47
You can exploit the fact that ArrayList reuses the object array generated by the Collection passed into its constructor.
This allows you to modify the contents of the arraylist after it has been created (although i don’t think you can change its size using this approach).
Paul Macdona wrote on 2012-03-08 14:56
Better watch out for evil luggage collections fiddling with your arrays…
I erred on the complicated side I guess, as I completely defined a dirty implementation of the Collection interface. The tricky bit was that toArray must return an Object[], not a String[], for the ArrayList constructor to use it.
public class Passenger
{
public static void main( String[] args ) throws Exception {
final Object[] a = new Object[]{“ice”};
Luggage luggage = new Luggage( new ArrayList() {
@Override
public Object[] toArray() {
return a;
}
});
a[0] = “liquid water”;
luggage.fly();
}
}
My first attempt… hide the data in secret compartment, then after the security checkpoint, hijack and innocently looking method to add it back to the luggage. But, the Luggage.class defeats this by wrapping my collection.
I haven’t read the solution yet, not giving up quite yet!
So looking for a better method to override, I looked at the source for ArrayList:
But, for whatever reason, ArrayList.toArray is smart enough to not allow someone control over it’s own array:
So all I have to do is override toArray:
This probably should be a bug in the JDK. I don’t think ArrayList should allow anyone control over it’s state!
Mohamed wrote on 2012-03-08 20:06
public static void main(String[] args) {
final Object[] data = new Object[]{“”};
Luggage l = new Luggage(new ArrayList(){
@Override
public Object[] toArray() {
return data;
}
});
data[0] = “liquid water”;
l.fly();
}
I prepared this as a separate blog-post, but given my missing-moderation-fluke (or is it actually better this way?), I’ll just add it here as a comment:
For those breaking their head over it: the for-loop, the Collection.contains and String.contains were just distractions to avoid making it too easy. Collections.unmodifiableCollection is also still perfectly safe.
The hole is in the ArrayList constructor. It’s implemented like this (in JDK 6 and 7):
There’s a check if the array returned from c.toArray() is of the right type. But nothing makes sure c doesn’t still hold a reference to that array. So the ArrayList can still be modified from the inside by modifying the array directly.
We create some perfectly safe luggage, and then teleport our water in:
Conclusion:
You’ll often see code code creating a defensive copy with new ArrayList<>(items), but in fact it’s not a completely safe copy. This is officially not a bug.
That’s not going to stop me from still using it though – it’s still good enough for all non-security-sensitive code.
20 comments
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
This should work, since the array is reused if it is instanceof Object[] …
Here it is
This one is not so hard. The trick is that the ArrayList constructor calls toArray() on the original collection and uses that result as a backing array. With some simple code, we can get hold of that array and modify it after the security check.
Good explanation!
All you need is a class implementing the Collection interface whose return value of toArray() is under your control. For shortness I used ArrayList as a base class instead of implementing all the methods of the Collection interface myself.
I did almost the same as Ives, but by creating a Collection that replaces instead of adds.
You can exploit the fact that ArrayList reuses the object array generated by the Collection passed into its constructor.
This allows you to modify the contents of the arraylist after it has been created (although i don’t think you can change its size using this approach).
Better watch out for evil luggage collections fiddling with your arrays…
The joy of Serialization!
Does this work for you with the security manager enabled?
enableReplaceObject
requires a specific permission!I erred on the complicated side I guess, as I completely defined a dirty implementation of the Collection interface. The tricky bit was that toArray must return an Object[], not a String[], for the ArrayList constructor to use it.
package liquid;
import java.util.ArrayList; import java.util.Collection;
public class Main { public static void main(String[] argv) { Object[] magicLuggagesContent = { “solid water” };
This is implementation dependent, but works.
Easy one :
package you;
import java.util.ArrayList;
import liquid.Luggage;
public class Passenger { public static void main( String[] args ) throws Exception { final Object[] a = new Object[]{“ice”}; Luggage luggage = new Luggage( new ArrayList() { @Override public Object[] toArray() { return a; } }); a[0] = “liquid water”; luggage.fly(); } }
My first attempt… hide the data in secret compartment, then after the security checkpoint, hijack and innocently looking method to add it back to the luggage. But, the Luggage.class defeats this by wrapping my collection.
I haven’t read the solution yet, not giving up quite yet!
So looking for a better method to override, I looked at the source for ArrayList:
But, for whatever reason, ArrayList.toArray is smart enough to not allow someone control over it’s own array:
So all I have to do is override toArray:
This probably should be a bug in the JDK. I don’t think ArrayList should allow anyone control over it’s state!
public static void main(String[] args) { final Object[] data = new Object[]{“”}; Luggage l = new Luggage(new ArrayList(){ @Override public Object[] toArray() { return data; } }); data[0] = “liquid water”; l.fly(); }
I prepared this as a separate blog-post, but given my missing-moderation-fluke (or is it actually better this way?), I’ll just add it here as a comment:
For those breaking their head over it: the for-loop, the
Collection.contains
andString.contains
were just distractions to avoid making it too easy.Collections.unmodifiableCollection
is also still perfectly safe. The hole is in theArrayList
constructor. It’s implemented like this (in JDK 6 and 7):There’s a check if the array returned from
c.toArray()
is of the right type. But nothing makes surec
doesn’t still hold a reference to that array. So theArrayList
can still be modified from the inside by modifying the array directly.We create some perfectly safe luggage, and then teleport our water in:
Conclusion: You’ll often see code code creating a defensive copy with
new ArrayList<>(items)
, but in fact it’s not a completely safe copy. This is officially not a bug.That’s not going to stop me from still using it though – it’s still good enough for all non-security-sensitive code.
The two contains() calls and the getItems() method did indeed distract me initially.
And since you’re asking: I preferred the previous format where comments were hidden until the solution is revealed.
I confirm this :D I looked at this to begin with.
And hiding comment is better as well.
Where is the water? :-)
Oops, I just teleported it into my comment :)
Well, again I have learned something new:
To discover method(s) from original list that were called and that we could exploit, I used dynamic proxy API. Here is my solution:
Definitely the most unique solution here, props! :)
But will this run with the security manager enabled?
Yes, dynamic proxy can be used with -Djava.security.manager paramter.