SSRF & 网络请求
前言
记载一下 SSRF, 顺便记载一下 HttpClient 发送 HTTP 包, 一方面是 SSRF 漏洞挖掘时会有这方面的涉及, 一方面在编写工具时难免会有发包需求.
URLConnection
URLConnection 是 JDK 自带的一个抽象类, 当然也是原生发送请求的一个类, 当然这个请求是通过什么协议具体也要看 URLConnection 的实现.
发送 HTTP 请求
在原生的 Java 程序中, 我们可以编写如下代码进行发送 HTTP 请求:
URL url = new URL("http://www.baidu.com/");
URLConnection urlConnection = url.openConnection();
try (
// 实现了 AutoCloseable 接口, 可以在 try(...) 中进行定义流, 在使用完毕后会自动关闭流
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
StringBuilder res = new StringBuilder(); // 等待追加
String tmp = "";
while ((tmp = br.readLine()) != null) {
res.append(tmp).append("n"); // 追加字符串信息
}
System.out.println(res); // 响应结果
}
这个案例很简单, 会直接向百度发送一次http请求, 并且通过流的方式将响应结果拿到了.
URL 初始化 & openConnection 运行逻辑
那么下面我们来看一下URL
类做了一些什么事情, 并且是如何得到URLConnection
的.
这里最终会通过URL::getURLStreamHandler
进行初始化handler
成员属性, 那么我们看一下这个方法做了一些什么事情:
而最后一行的return
最终会设置到URL
类的handler
成员属性中, 并且调用其parseURL
方法:
this.handler = handler;
handler.parseURL(this, spec, start, limit); // 对 URL 的解析, 就不再追源代码了
从代码中可以观察出如下三点:
-
最终会实例化 sun.net.www.protocol.当前协议.Handler
类, 而sun.net.www.protocol
这个包的定义我们可以在rt.jar
中进行找到. -
这些 Handler
类都为URLStreamHandler(抽象类)
实例. -
最终将生成的 Handler
实例放入到URL::handler
成员属性中, 也会放入到URL::handlers
这个Hashtable
中.
那么我们可以看一下URL::openConnection
方法的定义:
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this); // 会调用到具体 Handler 的 openConnection 方法
}
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream(); // 间接调用 openConnection
}
这些实际上是调用的Handler::openConnection
方法, 具体逻辑如下:
SSRF 利用
所以这里我们利用的协议是有限的, 通常可以利用如下协议:
file ftp mailto http https jar netdoc gopher(1.7可利用但需条件)
这里多了一个gopher
万金油协议出来, 其原因则是, 在JDK1.7
中gopher
是存在的, 但是需要具体的配置, 具体可以参考: https://bugzilla.redhat.com/show_bug.cgi?id=865541 中:
而在JDK1.8
中, 我们已经无法对其进行利用了, 所以在Java - SSRF
漏洞利用中, 显得特别鸡肋, 它不能像PHP那么灵活, 在这里可利用的协议有: file, http
协议.
file 协议
当然可以利用该协议进行读取文件信息:
URL url = new URL("file:///C:/Windows/win.ini");
URLConnection urlConnection = url.openConnection();
try (
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
StringBuilder res = new StringBuilder();
String tmp = "";
while ((tmp = br.readLine()) != null) {
res.append(tmp); // 追加字符串信息
}
System.out.println(res); // ; for 16-bit app support[fonts][extensions][mci extensions][files][Mail]MAPI=1
}
与PHP - SSRF - file协议利用
的不同点是, 这里的file
协议是可以进行列目录的, 编写如下代码进行测试:
URL url = new URL("file:///C:/"); // 列出 C 盘目录信息
URLConnection urlConnection = url.openConnection();
try (
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
StringBuilder res = new StringBuilder();
String tmp = "";
while ((tmp = br.readLine()) != null) {
res.append(tmp).append("n"); // 追加字符串信息
}
System.out.println(res);
/*
$Recycle.Bin
$WINRE_BACKUP_PARTITION.MARKER
Documents and Settings
DumpStack.log
DumpStack.log.tmp
hiberfil.sys
Intel
pagefile.sys
PerfLogs
ProgramData
Program Files
Program Files (x86)
Recovery
swapfile.sys
System Volume Information
Users
Windows
壁纸
*/
}
但如果被强制转换为HttpURLConnection
, 那么我们就无法再利用file
协议做一些事情了, 会抛出异常:
URL url = new URL("file:///C:/Windows/win.ini");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
/* 异常如下:
java.lang.ClassCastException: sun.net.www.protocol.file.FileURLConnection cannot be cast to java.net.HttpURLConnection
*/
http 协议
在这里我们当然可以进行探测内网的http
协议了, 在这里笔者开启了一个80
端口的本地服务:
URL url = new URL("http://127.0.0.1:80/"); // 本地是开放端口的
URLConnection urlConnection = url.openConnection();
try (
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
StringBuilder res = new StringBuilder();
String tmp = "";
while ((tmp = br.readLine()) != null) {
res.append(tmp).append("n");
}
System.out.println(res); // Hello World
}
当然笔者本地也开启了3306
端口的一个mysql
服务, 当代码逻辑中向外抛出了java.io.IOException: Invalid Http response
异常时, 我们可以进行监听内网其他非WEB
服务的端口:
URL url = new URL("http://127.0.0.1:3306/"); // 3306 是 MYSQL 服务
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try (
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8)); // java.io.IOException: Invalid Http response
) {
StringBuilder res = new StringBuilder();
String tmp = "";
while ((tmp = br.readLine()) != null) {
res.append(tmp).append("n");
}
System.out.println(res);
}
具体是否可以进行探测非web
服务还需要其具体的代码逻辑是否向外抛出了异常才可以.
当然也可以进行ntlmrelay
攻击, 参考: https://cloud.tencent.com.cn/developer/article/1937699 & https://xlab.tencent.com/cn/2019/03/18/ghidra-from-xxe-to-rce/
使用封装好的
HttpClient
,OkHttp
只可以进行发送HTTP
请求, 不允许使用file
协议.
HttpClient
HttpClient 是 Apache Jakarta Common下的一个子项目, 它提供了高效的、最新的、功能丰富的HTTP协议客户端编程工具包.
不同类型请求方式
为了能看到完整报文, 在这里笔者使用 nc 进行监听, 看一下每次调用 API 的结果. 使用 nc 返回如下结果进行调试:
HTTP/1.1 200 OK
Date: Wed, 15 Nov 2023 06:25:24 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11
HelloWorld!
当然使用 HttpClient 则需要引入对应依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
get 请求
普通 GET 请求
准备如下代码:
// 准备要请求的 URL
String url = "http://127.0.0.1:2333/";
try (
// 创建可关闭的 httpClient 客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
) {
// 准备 GET 请求对象
HttpGet httpGet = new HttpGet(url);
try (
// 准备响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
) {
// 得到响应结果
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
// 打印响应结果
System.out.println(result); // HelloWorld!
// 关闭流
EntityUtils.consume(entity);
}
}
最终 nc 可以收到如下请求:
C:UsersAdministrator>nc -lvvp 2333
GET / HTTP/1.1
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate
HTTP/1.1 200 OK
Date: Wed, 15 Nov 2023 06:25:24 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11
HelloWorld!
sent 123, rcvd 150
这里可以看到使用HttpClient
发送的请求头.
伪造 Header 头
调用HttpGet::addHeader
方法即可, 准备如下代码:
// 准备要请求的 URL
String url = "http://127.0.0.1:2333/";
// 创建可关闭的 httpClient 客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
// 准备 GET 请求对象
HttpGet httpGet = new HttpGet(url);
// 增加请求头
httpGet.addHeader("User-Agent", "Tester By Heihu577");
httpGet.addHeader("Referer", "localhost?");
// 准备响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
// 得到响应结果
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
// 打印响应结果
System.out.println(result); // HelloWorld!
// 关闭流
EntityUtils.consume(entity);
// 关闭结果
response.close();
// 关闭客户端
httpClient.close();
结果:
GET / HTTP/1.1
User-Agent: Tester By Heihu577
Referer: localhost?
Host: 127.0.0.1:2333
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
参数设置
原来代码的基础上, 拼接上参数即可:
// 准备参数, 并使用 URLEncode 进行一层编码
String password = URLEncoder.encode( "1 2+3|A_bc", "UTF-8");
// 准备要请求的 URL
String url = "http://127.0.0.1:2333/login?username=Heihu577&password=" + password;
收到请求:
GET /login?username=Heihu577&password=1+2%2B3%7CA_bc HTTP/1.1
User-Agent: Tester By Heihu577
Referer: localhost?
Host: 127.0.0.1:2333
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
获取响应头
CloseableHttpResponse.getStatusLine().getStatusCode()
方法的调用以及CloseableHttpResponse.getHeaders()
的调用.
// 准备参数, 并使用 URLEncode 进行一层编码
String password = URLEncoder.encode("1 2+3|A_bc", "UTF-8");
// 准备要请求的 URL
String url = "http://127.0.0.1:2333/login?username=Heihu577&password=" + password;
// 创建可关闭的 httpClient 客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
// 准备 GET 请求对象
HttpGet httpGet = new HttpGet(url);
// 增加请求头
httpGet.addHeader("User-Agent", "Tester By Heihu577");
httpGet.addHeader("Referer", "localhost?");
// 准备响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
// 判断响应头是不是 200
if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
// 得到服务端返回的响应头
Header[] allHeaders = response.getAllHeaders();
for (Header header : allHeaders) {
System.out.println(header.getName() + ":" + header.getValue());
}
// 得到响应结果
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
// 打印响应结果
System.out.println(result); // HelloWorld!
// 关闭流
EntityUtils.consume(entity);
}
// 关闭结果
response.close();
// 关闭客户端
httpClient.close();
判断响应头, 获取响应头等信息.
保存响应体 (存储二进制值)
准备一张知乎的图片, 然后保存下来, 主要是对EntityUtils.toByteArray
API的调用:
CloseableHttpClient httpClient = HttpClients.createDefault();
String url = "https://pic4.zhimg.com/v2-267217db1a16cf81a50184ea7d520289_1440w.jpg";
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
byte[] byteArray = EntityUtils.toByteArray(entity); // 调用 API
new FileOutputStream("./img.jpg").write(byteArray); // 写入到 ./img.jpg
EntityUtils.consume(entity);
httpClient.close();
response.close();
设置访问代理 & 链接超时 & 读取超时
核心代码:
RequestConfig.Builder custom = RequestConfig.custom();
custom.setProxy(new HttpHost("127.0.0.1", 8080)); // 可以设置成 BurpSuite 的代理
custom.setConnectTimeout(5000); // 连接超时, 单位毫秒, 5秒链接超时则报错
custom.setSocketTimeout(5000); // 读取超时, 5秒没有得到响应, 则抛出异常 (可以使用BP抓到包后不放包看到效果)
httpGet.setConfig(custom.build());
在这里可以使用Bp
抓到代理结果:
post 请求
参数 & Content-Type 设置
我们知道的是, POST 请求的 Content-Type
为application/x-www-form-urlencoded
, 所以在这里使用HttpClient
发送该类型的请求:
String url = "http://127.0.0.1:2333/?id=1";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url); // 使用 POST, 仅仅只是改变请求方式: POST / HTTP/1.1
List<NameValuePair> params = new Vector<>(); // 准备 POST 发送的主体参数
params.add(new BasicNameValuePair("name", "heihu577")); // POST发送的值: name=heihu577
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params); // 修改 ContentType: application/x-www-form-urlencoded
httpPost.setEntity(urlEncodedFormEntity); // 设置 Entity
CloseableHttpResponse execute = httpClient.execute(httpPost);
HttpEntity entity = execute.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
System.out.println(result);
httpClient.close();
nc 可以收到如下请求:
POST /?id=1 HTTP/1.1
Content-Length: 13
Content-Type: application/x-www-form-urlencoded
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate
name=heihu577
当然也可以通过HttpPost::addHeader
进行设置, 就不再掩饰了.
json 类型请求
json 的 Content-Type 为 application/json
, 这里不能再使用HttpClient
提供的Entity
了, 使用提供的原生的StringEntity
.
首先我们需要引入json
依赖, 这里使用 jackson:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
进行编码, 发送该类型请求:
String url = "http://127.0.0.1:2333/?id=1";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url); // 使用 POST, 仅仅只是改变请求方式: POST / HTTP/1.1
ObjectMapper objectMapper = new ObjectMapper(); // jackson 所需要的引用
HashMap<Object, Object> hsmap = new HashMap<>(); // 通过 HashMap 来生成一个 Json 串
hsmap.put("name", "heihu577");
hsmap.put("age", 12);
String jsonText = objectMapper.writeValueAsString(hsmap); // {"name":"heihu577","age":12}
StringEntity stringEntity = new StringEntity(jsonText, StandardCharsets.UTF_8); // 设置 POST 中的值
stringEntity.setContentType("application/json"); // 手动设置 Content-Type
httpPost.setEntity(stringEntity); // 设置 Entity
CloseableHttpResponse execute = httpClient.execute(httpPost);
HttpEntity entity = execute.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
System.out.println(result);
httpClient.close();
最终 nc 收到如下请求:
POST /?id=1 HTTP/1.1
Content-Length: 28
Content-Type: application/json
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate
{"name":"heihu577","age":12}
上传请求
上传请求Content-Type
为multipart/form-data
, 以及它的请求体是庞大的. 那么下面我们来看一下如何构造上传表单, 首先引入所需的依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
随后编码:
String url = "http://localhost:808/testFile";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url); // 使用 POST, 仅仅只是改变请求方式: POST / HTTP/1.1
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); // 定义一个上传表单
multipartEntityBuilder.setCharset(StandardCharsets.UTF_8); // 设置编码为 UTF-8
multipartEntityBuilder.addTextBody("name", "heihu577"). // 增加参数
addBinaryBody("file", new File("C:/Windows/win.ini"), ContentType.create("text/plain", Consts.UTF_8), "win.ini"). // 增加上传文件, 也可以不指定 content-type 以及文件名
addPart("file2", new StringBody("This is text, 简单文本~", ContentType.create("text/plain", Consts.UTF_8))); // 自定义一个上传流
multipartEntityBuilder.setContentType(ContentType.create("multipart/form-data", Consts.UTF_8)); // 设置为上传表单
multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); // 设置为浏览器上传模式
HttpEntity httpEntity = multipartEntityBuilder.build(); // 通过 MultipartEntityBuilder 来 build 一个 Entity
httpPost.setEntity(httpEntity); // 设置 Entity
CloseableHttpResponse execute = httpClient.execute(httpPost);
HttpEntity entity = execute.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
System.out.println(result);
httpClient.close();
最终 nc 收到的请求为:
POST /upfile HTTP/1.1
Content-Length: 510
Content-Type: multipart/form-data; boundary=aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY; charset=UTF-8
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate
--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY
Content-Disposition: form-data; name="name"
heihu577
--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY
Content-Disposition: form-data; name="file"; filename="win.ini"
Content-Type: text/plain; charset=UTF-8
; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1
--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY
Content-Disposition: form-data; name="file2"
This is text, 简单文本~
--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY--
其他常用功能
登陆携带cookie访问下一个连接
创建setcookie.php
文件内容如下:
<?php
setcookie('name', 'heihu577');
创建getcookie.php
文件内容如下:
<?php
echo isset($_COOKIE['name']) ? $_COOKIE['name'] : 'NoCookie';
若服务器最终得到的是heihu577
, 则意味着cookie
被成功配置到服务器端.
准备如下代码:
// 创建一个Cookie存储对象
CookieStore cookieStore = new BasicCookieStore();
// 创建一个支持Cookie的HttpClient
CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
// 访问后 cookieStore中将保存服务器发送的Cookie, 接下来可以使用这些Cookie访问受保护的资源
httpClient.execute(new HttpGet("http://localhost/setcookie.php"));
// 访问 getcookie.php
CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost/getcookie.php"));
HttpEntity entity = response.getEntity();
// 最终得到Cookie: heihu577
System.out.println(EntityUtils.toString(entity));
// 关闭一些资源...
EntityUtils.consume(entity);
response.close();
httpClient.close();
绕过 https 安全认证
在这里我们获取CloseableHttpClient
不能再像往常一样这样获取:
CloseableHttpClient httpClient = HttpClients.createDefault();
而是改为如下获取方式:
CloseableHttpClient httpClient = createHttpClientWithNoSsl();
其中createHttpClientWithNoSsl
方法定义如下:
public static CloseableHttpClient createHttpClientWithNoSsl() throws Exception {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// don't check
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// don't check
}
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx);
return HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.build();
}
连接池配置
配置如下:
public class HttpUtilTest {
public static CloseableHttpClient createClient() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(100); // 最大连接数
cm.setDefaultMaxPerRoute(20); // 每个路由的默认最大连接数
return HttpClients.custom()
.setConnectionManager(cm)
.build();
}
@Test
public void t1() {
long l = System.currentTimeMillis();
CloseableHttpClient client = createClient(); // 可以观察到与 HttpClients.createDefault() 的区别
for (int i = 0; i < 20; i++) {
try {
HttpGet get = new HttpGet("http://www.baidu.com");
CloseableHttpResponse response = client.execute(get);
System.out.println(response.getStatusLine());
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("耗时:" + (System.currentTimeMillis() - l));
}
}
连接池 + https 绕过安全认证
在这里如果往常配置会报错, 参考: https://blog.csdn.net/u013310119/article/details/81906571 给出解决方案, 在构造方法中进行配置即可, 最终CloseableHttpClient
初始化代码如下:
public static CloseableHttpClient createClient() throws KeyManagementException, NoSuchAlgorithmException {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// don't check
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// don't check
}
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx);
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory).build());
cm.setMaxTotal(100); // 最大连接数
cm.setDefaultMaxPerRoute(20); // 每个路由的默认最大连接数
return HttpClients.custom()
.setConnectionManager(cm)
.build();
}
Ending...
https://www.jb51.net/article/240169.htm
https://www.cnblogs.com/wgslucky/p/11270038.html
https://isenninha.github.io/2020/11/12/http-client/
https://blog.csdn.net/u013310119/article/details/81906571
原文始发于微信公众号(Heihu Share):开发 & 安全 | SSRF[URLConnection] 与 HttpClient
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论