提交 5bb0ed5c 编写于 作者: J Jesse Glick

Reproduced original exploit. Framework of round tests, not yet confirmed to work.

上级 15032553
......@@ -26,8 +26,12 @@ package jenkins.security;
import hudson.cli.CLI;
import hudson.cli.CliPort;
import hudson.remoting.BinarySafeStream;
import hudson.util.DaemonThreadFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
......@@ -37,7 +41,14 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import jenkins.util.Timer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
......@@ -46,14 +57,23 @@ import org.jvnet.hudson.test.recipes.PresetData;
public class Security218BlackBoxTest {
private static final ExecutorService executors = Executors.newCachedThreadPool(new DaemonThreadFactory());
@Rule
public JenkinsRule r = new JenkinsRule();
@PresetData(PresetData.DataSet.NO_ANONYMOUS_READACCESS)
@Test
public void probe() throws Exception {
File pwned = new File("/tmp/pwned"); // TODO fix payload() to set a system property and test that instead
final AtomicInteger round = new AtomicInteger();
final AtomicBoolean foundRound = new AtomicBoolean();
do {
foundRound.set(false);
System.err.println("Round #" + round);
FileUtils.deleteQuietly(pwned);
final ServerSocket proxySocket = new ServerSocket(0);
Timer.get().submit(new Runnable() {
executors.submit(new Runnable() {
@Override
public void run() {
try {
......@@ -65,7 +85,7 @@ public class Security218BlackBoxTest {
final OutputStream realOS = real.getOutputStream();
final InputStream proxyIS = proxy.getInputStream();
final OutputStream proxyOS = proxy.getOutputStream();
Timer.get().submit(new Runnable() {
executors.submit(new Runnable() {
@Override
public void run() {
try {
......@@ -127,15 +147,16 @@ public class Security218BlackBoxTest {
}
}
});
Timer.get().submit(new Runnable() {
executors.submit(new Runnable() {
@Override
public void run() {
try {
int nullCount = 0;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ByteArrayOutputStream toCopy = new ByteArrayOutputStream();
int c;
int nullCount = 0;
while ((c = proxyIS.read()) != -1) {
realOS.write(c);
toCopy.write(c);
buf.write(c);
if (c == 0) {
if (++nullCount == 4) {
......@@ -145,19 +166,34 @@ public class Security218BlackBoxTest {
nullCount = 0;
}
}
if (round.get() == 0) {
foundRound.set(true);
System.err.println("injecting payload into capability negotiation");
// replacing \x00\x14Protocol:CLI-connect<===[JENKINS REMOTING CAPACITY]===>rO0ABXNyABpodWRzb24ucmVtb3RpbmcuQ2FwYWJpbGl0eQAAAAAAAAABAgABSgAEbWFza3hwAAAAAAAAAP4=\x00\x00\x00\x00
new DataOutputStream(realOS).writeUTF("Protocol:CLI-connect"); // TCP agent protocol
byte[] PREAMBLE = "<===[JENKINS REMOTING CAPACITY]===>".getBytes("UTF-8"); // Capability
realOS.write(PREAMBLE);
OutputStream bss = BinarySafeStream.wrap(realOS);
bss.write(payload());
bss.flush();
} else {
System.err.print("→ ");
display(buf.toByteArray());
System.err.println();
realOS.write(toCopy.toByteArray());
}
int packet = 0;
PACKETS: while (true) {
buf.reset();
toCopy.reset();
while (true) {
int hi = proxyIS.read();
if (hi == -1) {
break PACKETS;
}
realOS.write(hi);
toCopy.write(hi);
int lo = proxyIS.read();
realOS.write(lo);
toCopy.write(lo);
boolean hasMore = (hi & 0x80) > 0;
if (hasMore) {
hi &= 0x7F;
......@@ -165,17 +201,27 @@ public class Security218BlackBoxTest {
int len = hi * 0x100 + lo;
for (int i = 0; i < len; i++) {
c = proxyIS.read();
realOS.write(c);
toCopy.write(c);
buf.write(c);
}
if (hasMore) {
continue;
}
if (++packet == round.get()) {
foundRound.set(true);
System.err.println("injecting payload into packet");
byte[] data = payload();
realOS.write(data.length / 256);
realOS.write(data.length % 256);
realOS.write(data);
} else {
System.err.print("→ ");
byte[] data = buf.toByteArray();
//display(data);
showSer(data);
System.err.println();
realOS.write(toCopy.toByteArray());
}
break;
}
}
......@@ -189,15 +235,30 @@ public class Security218BlackBoxTest {
}
}
});
try {
executors.submit(new Runnable() {
@Override
public void run() {
// Bypassing _main because it does nothing interesting here.
// Hardcoding CLI protocol version 1 (CliProtocol) because it is easier to sniff.
try {
new CLI(r.getURL()) {
@Override
protected CliPort getCliTcpPort(String url) throws IOException {
return new CliPort(new InetSocketAddress(proxySocket.getInetAddress(), proxySocket.getLocalPort()), /* ignore identity */null, 1);
return new CliPort(new InetSocketAddress(proxySocket.getInetAddress(), proxySocket.getLocalPort()), /* ignore identity */ null, 1);
}
}.execute("help");
fail("TODO assert that payloads did not work");
} catch (Exception x) {
x.printStackTrace();
}
}
}).get(15, TimeUnit.SECONDS);
} catch (TimeoutException x) {
System.err.println("CLI command timed out");
}
assertFalse("Pwned!", pwned.isFile());
round.incrementAndGet();
} while (foundRound.get());
}
private static synchronized void display(byte[] data) {
......@@ -220,4 +281,15 @@ public class Security218BlackBoxTest {
}
}
/** An attack payload, as a Java serialized object ({@code \xAC\ED…}). */
private static byte[] payload() throws IOException {
// TODO from ysoserial; use that library to generate other payloads on demand (would like a variant which uses System.setProperty for more portable tests)
InputStream is = Security218BlackBoxTest.class.getResourceAsStream("payload.ser");
try {
return IOUtils.toByteArray(is);
} finally {
is.close();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册