javasecRMI

Posted 海屿-uf9n1x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javasecRMI相关的知识,希望对你有一定的参考价值。

这篇文章介绍java-RMI远程方法调用机制。

RMI全称是Remote Method Invocation,远程⽅法调⽤。是让某个Java虚拟机上的对象调⽤另⼀个Java虚拟机中对象上的⽅法,只不过RMI是Java独有的⼀种RPC方法。看这篇之前可以先去看看RPC:https://www.bilibili.com/video/BV1zE41147Zq?from=search&seid=13740626242455157002

RMI流程

RMI远程⽅法调⽤的流程:

先介绍各个部分的功能作用:

  • Stub:客户端调用一个被称为 Stub (存根)的客户端代理对象。该代理对象负责对客户端隐藏网络通讯的细节。Stub 写着如何通过网络套接字(Socket)发送调用,包括如何将调用参数转换为适当的形式以便传输等。简单理解就是封装了一层网络传输的细节,直接传入参数调用就行。
  • Skeleton:在服务端中该代理对象负责对分布式对象隐藏网络通讯的细节。Skeleton 知道如何从网络套接字(Socket)中接受调用,包括如何将调用参数从网络传输形式转换为 Java 形式等。
  • Registry:注册中心,服务端在注册中心注册服务时,需要提供一个key以及一个value,这个value是一个远程对象,Registry会对这个远程对象进行封装,使其转为一个远程代理对象,它本身不会执行方法。在低版本的JDK中,Server与Registry是可以不在一台服务器上的,而在高版本的JDK中,Server与Registry只能在一台服务器上,否则无法注册成功
  • 注:
  1. Java对远程访问RMI Registry做了限制,只有来源地址是localhost的时候,才能调用rebind、bind、unbind等方法。
  2. 不过list和lookup方法可以远程调用。
  3. list方法可以列出目标上所有绑定的对象;lookup作用就是获得某个远程对象。

那么,只要目标服务器上存在一些危险方法,我们通过RMI就可以对其进行调用。

利用工具:https://github.com/NickstaDB/BaRMIe

建立RMI的流程如下

  1. 通过分析需求定义远程接口(客户端和服务器端公用的),此接口必须扩展java.rmi.Remote,且远程方法必须声明抛出 java.rmi.RemoteException 异常,或者该异常的超类(Superclass)
  2. 服务器端实现远程接口,为了不手动生成stub需要继承UnicastRemoteObject类,并调用其构造器;
  3. 服务器端注册服务并启动;
  4. 客户端查询服务并调用远程方法;

代码实现

分别建立三个项目:服务器端(RMIDemoServer)、客户端(RMIDemoClient)和远程接口(DemoRMI.RmoteInterface)

DemoRMI.RmoteInterface

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloRMI extends Remote 

    String sayHello(String name) throws RemoteException;

//继承了 java.rmi.Remote 的接⼝,其中定义我们要远程调⽤的函数,⽐如这⾥的  sayHello(String name)

RMIDemoServer

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

@SuppressWarnings("serial")
public class RMIHelloImpl extends UnicastRemoteObject implements HelloRMI 

    protected RMIHelloImpl() throws RemoteException 
        super();
    

    public String sayHello(String name) 
        return "Hello,"+name;
    


//实现了接⼝的类 这里输出Hello 传入的值
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class SerApp

    public static void main( String[] args ) throws RemoteException, MalformedURLException
    
        HelloRMI hello= (HelloRMI) new RMIHelloImpl();

        LocateRegistry.createRegistry(1099);
        Naming.rebind("rmi://127.0.0.1/hello", hello);
        System.out.println("Server ok");
    


//创建Registry,并将实现类实例化后绑定到⼀个地址。这两部分就是我们所谓的Server

RMIDemoClient

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class ClientApp 
    public static void main( String[] args ) throws MalformedURLException, RemoteException, NotBoundException
    
        HelloRMI hello=(HelloRMI) Naming.lookup("rmi://127.0.0.1/hello");
        System.out.println( hello.sayHello("Roderick RMI"));
    


//客户端就简单多了,使⽤ Naming.lookup 在Registry中寻找到名字是hello的对象,调用hello.sayHello

简单说一下这个流程:首先定义公共的接口HelloRMI,然后服务端创建实现类RMIHelloImpl,同时创建Registry并将实现类实例化后绑定到⼀个地址。这样RMI的Server就算完成了,再直接启动SerApp。客户端直接用Naming.lookup去访问Registry获取对象,然后像使用本地方法一样使用RMI来的方法即可方法。启动ClientApp可以看到完成了RMI整个调用过程。

借用p牛的解说:

“过程进行了了两次TCP握手,也就是我们实际建立了两次TCP连接。第⼀一次建立TCP连接是连接远端的1099端口,这也是我们在代码⾥里里看到的端口,⼆二者进行沟通后,我向远端发送了一个“Call”消息,远端回复了一个“ReturnData”消息,然后我新建了一个TCP连接,连到远端的33769端口。所以捋一捋这整个过程,首先客户端连接Registry,并在其中寻找Name是Hello的对象,这个对应数据流中的Call消息;然后Registry返回一个序列化的数据,这个就是找到的Name=hello的对象,这个对应数据流中的ReturnData消息;客户端反序列化该对象,发现该对象是一个远程对象,地址在x.x.x.x:端口,于是再与这个地址建⽴立TCP连接;在这个新的连接中,才执行真正远程方法调⽤用,也就是hello()。”

借用下图来说明这些元素间的关系:

RMI Registry就像一个网关,他自己是不不会执行远程方法的,但RMI Server可以在上⾯面注册一个Name到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMIServer;最后,远程方法实际上在RMI Server上调用。

一个枚举和攻击 Java RMI(远程方法调用)服务的工具:https://github.com/NickstaDB/BaRMIe

iwebsec靶场 SQL注入漏洞通关笔记13-二次注入

系列文章目录

iwebsec靶场 SQL注入漏洞通关笔记1- 数字型注入_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记2- 字符型注入(宽字节注入)_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记3- bool注入(布尔型盲注)_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记4- sleep注入(时间型盲注)_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记5- updatexml注入(报错型盲注)_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记6- 宽字节注入_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记7- 空格过滤绕过_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记8- 大小写过滤注入_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记9- 双写关键字绕过_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记10- 双重url编码绕过_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记11-16进制编码绕过_mooyuan的博客-CSDN博客

iwebsec靶场 SQL注入漏洞通关笔记12-等价函数替换绕过_mooyuan的博客-CSDN博客


目录

系列文章目录

前言

一、二次注入

1.漏洞原理

2.漏洞原因

3.二次注入思路

二、源码分析

1.注册界面index.php

2.找回密码 reset.php

3.确定渗透目标

4.二次注入渗透分析

三、渗透获取admin账号密码

1.注册

 2.获取admin密码

四、渗透获取user1账号密码

1.注册

2.找回密码

总结


前言

打开靶场,url为 http://192.168.71.151/sqli/13/index.php 如下所示是一个注册界面,参数为用户名、密码和邮箱,源码猜测是SQL insert语句,将其插入到数据库的某个表中。

 点击通过邮箱找回密码进入如下界面,根据功能应该是通过邮箱找到用户名和密码,并展示出来

一、二次注入

1.漏洞原理

SQL二次注入漏洞指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。相对而言这是一种较难发现的SQL安全漏洞形式。

2.漏洞原因

造成二次SQL注入漏洞的原因如下:网站对在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据(恶意构造的SQL语句)。

3.二次注入思路

    1.黑客通过insert语句功能的web环境构造构造二次注入命令,在浏览器或者其他软件中提交HTTP数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了黑客构造的SQL语句或者命令。
    2.服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。
    3.黑客在查询功能的web环境中再次向服务端发送第二个请求数据信息。
    4.服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的SQL语句或者命令在服务端环境中执行,从而形成二次注入攻击。
    5.服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。

二、源码分析

1.注册界面index.php

本关卡的源码如下,index.php包含注册功能,在注册insert的SQL语句调用前,可以看到对用户名、密码和邮件均调用了addslashes函数进行处理,这是二次注入漏洞存在的条件之一。

关于注册过滤的相关源码如下所示

	$username=addslashes($_POST['username']);
	$password=addslashes($_POST['password']);
	$email=addslashes($_POST['email']);	 
	$sql="INSERT INTO `sqli` (`username` ,`password` ,`email`)	VALUES ('$username', '$password', '$email');";
	$row = mysql_query($sql);
	if($row)
	 echo "<script>alert('注册成功')</script>";
	else
	 echo "<script>alert('注册失败')</script>";
	
	 

基于二次注入的条件,由于参数username、password和email均有addslashes处理,那么在逻辑上我们 三处都可以使用万能注入语句' or 1=1 # 进行尝试。不过到底选择使用哪个,我们还要分析找回邮箱部分的源码,以确定到底使用哪个注册参数作为二次注入的点。

2.找回密码 reset.php

找回密码的reset.php的源码如下所示,功能为先通过邮箱查询到用户名,再根据用户名把密码输出出来,于是与注册源码结合,就可以判断出来username为二次注入点。

相关基于SQL处理流程部分源码如下所示

if($_POST['sub'])
  $email=addslashes($_POST['email']);
  $sql="select * from sqli where email='$email'";


 $row=mysql_query($sql);
 if($row)
	$rows = mysql_fetch_array($row);
	$username=$rows['username'];
	$sql2="select * from sqli where username='$username'";

	$row=mysql_query($sql2) or die(mysql_error()); 
	$rows = mysql_fetch_array($row);
	if($rows)
	echo "<center><h4>您的用户名是 ".$rows['username']."</h4><br>";
	echo "<center><h4>您的密码是 ".$rows['password']."</h4><br>";
	else
	  
	  echo "<script>alert('邮箱不存在')</script>";
  
 

3.确定渗透目标

根据源码我们可以判断使用的是sqli表的内容,那么进入到iwebsec的docker容器环境中,执行如下命令来获取到iwebsec数据库中sqli表的内容

mysql -u iwebsec -p # 输入密码iwebsec
use iwebsec
select * from iwebsec

如下图所示,红框中admin账号的密码就是我们想通过SQL注入爆出来的内容,也是我们打算渗透的内容

4.二次注入渗透分析

先来考虑一下到底我们想爆出admin账户的密码,就是需要利用找回密码界面的SQL语句,将username作为二次注入点

select * from sqli where username='$username'

如上所示,此SQL语句的闭合方式为单引号,我们的目标是admin账号,那么构造SQL注入语句,此时username可以为如下内容(其中#和--空格为注释)

admin'#
admin'-- -

这时候我们再回到注册界面的SQL语句,由于username参数在SQL调用前执行了addslashes函数处理,那么我们如果在注册时username就构造为万能注入语句(admin'# 或者admin' -- -),就可以保存到数据库的sqli表中,从而形成二次注入

	 $username=addslashes($_POST['username']);
	 $password=addslashes($_POST['password']);
	 $email=addslashes($_POST['email']);	 
	 $sql="INSERT INTO `sqli` (`username` ,`password` ,`email`)	VALUES ('$username', '$password', '$email');";

三、渗透获取admin账号密码

1.注册

在注册界面usernmae处填精心构造的

amdin'#

 密码则随意填写,邮箱填ljn,接下来进行注册

点击注册后,提示注册成功

这时候我们在MySQL数据库里查询,可知注册成功

 2.获取admin密码

进入找回邮箱界面,输入刚刚注册admin'#账号时填写的邮箱ljn,点击OK后爆出了admin账号的用户名和密码,如下所示渗透成功

四、渗透获取user1账号密码

1.注册

为了与获取admin做区分,这次使用--空格的方式构造注入语句,用户名填user1-- - 密码随意,邮箱填llllll

点击注册后提示如下

注册成功后数据库信息如下所示

2.找回密码

进入密码找回界面输入llllll


输出user1账号的用户名和密码pass1

总结

SQL二次注入主要分析几个内容

(1)注入点是什么?iwebsec的第13关关卡的注入点为username

(2)注入点闭合方式与注入?这部分是普通的字符型注入,闭合方式为单引号

(3)是否满足二次注入?很明显通过源码分析符合二次注入条件

了解了如上信息就可以针对性进行SQL二次注入渗透,构造二次注入的语句,从而成功的爆破到想要的账号的密码。

以上是关于javasecRMI的主要内容,如果未能解决你的问题,请参考以下文章