urlConnection和ssrf
URLConnection
这是一个对网页发起请求的类,类似于之前跟数据库发起请求差不多。
首先建立连接对象,然后设置请求属性,然后将结果返回,并且利用InputStream保存,随后展示在页面上。
代码演示
package urlConnection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class Demo01 {
public static void main(String[] args) throws IOException {
URL url = new URL("https://www.baidu.com");
URLConnection urlConnection = url.openConnection();
//设置请求参数
urlConnection.setRequestProperty("user-agent", "javasec");
urlConnection.setConnectTimeout(1000);
urlConnection.setReadTimeout(1000);
// 建立实际连接
urlConnection.connect();
// 获取响应头字段信息列表
urlConnection.getHeaderFields();
// 获取URL响应
urlConnection.getInputStream();
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
response.append("\r\n").append(line);
}
System.out.print(response.toString());
}
}
这就是简单的一个urlconnection请求百度然后将结果展示在控制台的代码。这个代码在以后进行爬虫等操作的时候还是可以用到的。
在java中,Java抽象出来了一个urlConnection类,它用来表示应用程序以及url建立通信连接的所有类的超类,通过url类中的openConnection方法获取到的urlconnection的类对象。
urlConnection支持的协议有:file/ftp/gopher/http/https/jar/mailto/netdoc
在一个正常的网页之中,有的页面想要完成既定的功能,就必须要想外界发起请求,请求数据,在这个时候,如果对网址输入的位置没有做好足够的防护,则会造成ssrf漏洞。
SSRF漏洞
简单来说,ssrf漏洞就是在网页对内,或者对外发起请求,请求一些资源的时候,网址可以被用户控制,且么有对用户的输入进行限制,所以导致了ssrf漏洞的产生。
容易出现ssrf漏洞的位置是
- 抓取用户输入图片的地址并且本地化存储。
- 从远程服务器请求资源
- 对外发起网络请求
一般来说,在利用ssrf漏洞进行攻击的时候,有几种方式。
- 扫描内部网络,利用字典协议dict://进行端口探测
- 进行内网渗透(因为大部分情况下内网防护是要比外网防护弱的)
- 如果内网中主机存在redis,可以尝试利用跳板机来实现redis未授权访问getshell
- 可以利用file协议读取本机敏感文件
- 可以做内网流量代理,可以利用ssrf-proxy工具来完成。
- ntlm relay(中继)攻击
其他几个,都是比较常见的用法,这里我简单的说一下什么是中继攻击。
NTLM relay(中继)攻击
由于该Relay攻击要涉及到NTLM认证和Net-NTLM Hash,所以我们先来了解一下NTLM认证过程和什么是Net-NTLM Hash。
NTLM Hash:NTLM hash 就是里面加密保存了用户密码的 hash。Windows 中的用户密码被系统加密后保存在 SAM 文件中,如果是域环境则保存在域控的 NTDS.dit 中。
Net-NTLM Hash:Net-NTLM Hash 是基于用户密码的NTLM Hash计算出来的,用于在网络环境下 NTLM 认证的 hash。在下面的NTLM认证过程中你可以知道Net-NTLM Hash产生的过程。
NTLM认证过程
\1. 当客户端需要访问服务器时,客户端需要输入服务器的用户名和密码进行验证,并且客户端会将服务器的NTLM-Hash值缓存。之后客户端开始向服务器发送 TYPE 1 Negotiate 协商消息。
\2. 服务器收到客户端发送来的 TYPE 1 协商消息后,会取出其中自己能够接受的内容,传入NTLM SSP,得到 TYPE 2 挑战消息,此 TYPE 2消息中包含了一个由服务端生成的16位随机值,被称为 Challenge。服务器将此challenge保存一份后将TYPE 2消息发送回客户端。
\3. 客户端收到服务器发来的TYPE 2消息后,读出其中的challenge值,用缓存的服务端密码的NTLM-Hash对其进行加密,并与用户名、challenge等一起组合得到 Net-NTLMHash ,最后将 Net-NTLMHash 封装到 TYPE 3 NTLM_AUTH消息中发往服务器。
\4. 服务器在收到 TYPE 3 的消息之后,用自己密码的 NTLM-Hash 对 Challenge 进行加密,并比较自己计算出的 Net NTLM-Hash 认证消息和客户端发送的认证消息是否匹配。如果匹配,则证明客户端掌握了正确的密码,认证成功,否则认证失败。
如果是在域环境中,那么认证过程会经过域控制器:
\4. 服务器接收到客户端发送来的 TYPE 3 消息后,取出其中的Net NTLM-Hash值,并向域控制器发送针对客户端的验证请求。该请求的内容包含:用户名、原始的 Challenge 和 加密后的Challenge(也就是Net NTLM-Hash)。
\5. DC根据用户名取出该帐号的密码哈希值 NTLM-Hash,用密码哈希值 NTLM-Hash 对原始的Challenge进行加密得到Net NTLM-Hash。如果加密后的Challenge和服务器发送的一致,则意味着用户拥有正确的密码,验证通过,否则验证失败。DC将验证结果发给服务器。
服务器根据DC返回的结果,对客户端进行回复
在这里,如果我们获得了NTLM-Hash,那么我们可以直接进行PTH攻击,但不能用来Relay;而如果我们获得了Net NTLM-Hash,那么我们可以对其进行爆力破解得到明文,也可以利用Net NTLM-Hash进行中继攻击。
NTLM中继攻击原理
NTLM hash 分为 NTLMv1 NTLMv2 NTLM session v2 三种,NTLMv2 的强度比 NTLMv1 强了不少 ,我们在实战中,如果获得的是NTLMv1的话直接对其进行爆破就行了,而现实情况中我们遇到的是 NTLMv2,NTLMv2的密码强度高了不少,因此如果你没有一个超级强大的字典,你很难得到明文密码。那么,如果爆破行不通的话我们不妨试一下NTLM Relay攻击。
在这个NTLM Relay中,我们就是要将截获的Net-NTLM Hash重放来进行攻击,从而实现对其他机器的控制,所以严格意义上应该叫作Net-NTLM Relay。
SSRF漏洞的防护
根据之前的介绍,那代码中如何看出是否存在ssrf漏洞呢,简单来说,首先如果网站需要使用urlconnection发起请求,然后这个请求是可以被用户控制的,在发起这个请求之前,也没有对用户的输入做任何的限制,那么这个地方就可能存在ssrf漏洞。
举个例子,之前的按个代码,如果我们把url里面的内容从"https://www.baidu.com"
变成"file:///etc/passwd"
那么获取到的内容及会从百度的网页内容转变为passwd文件里面的值。
针对于这个点,就是因为我们没有对用户的输入进行限制造成的。
这里,如果我们把要请求的资源网站的网址不直接写在url里面,而是先设置一个变量存储,然后再需要使用的时候再调用这个变量拼接命令,那么这个问题就变得没那么难解决了。
修改之后的代码,就会变成这样:
package urlConnection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class Demo01 {
public static void main(String[] args) throws IOException {
String path = "https://wwww.baidu.com";
URL url = new URL(path);
URLConnection urlConnection = url.openConnection();
//设置请求参数
urlConnection.setRequestProperty("user-agent", "javasec");
urlConnection.setConnectTimeout(1000);
urlConnection.setReadTimeout(1000);
// 建立实际连接
urlConnection.connect();
// 获取响应头字段信息列表
urlConnection.getHeaderFields();
// 获取URL响应
urlConnection.getInputStream();
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
response.append("\r\n").append(line);
}
System.out.print(response.toString());
}
}
如果换成网页的话,情况也是一致的,get请求的方式去请求外部资源完成网站原始功能的这一点,首先,在我们设置这个网站的时候,我们需要去请求那个地方的资源是已经确定的,而且这个网站肯定也不会特别多。
那么我们直接为这个几个网址申请一些变量,当我们需要使用这些内容的时候,先通过get请求确定我们要请求的是那个网址,然后再将这个网址拼接到url请求里面,就可以避免url用户可控的情况出现。