很多 Ollama 服务部署在公网却没有安全机制,可以利用这一点白嫖资源。
利用方式
fofa.info 中搜索
1
| port="11434" && status_code="200" && body="Ollama" && region="TW"
|
上面的 body 和 region 是细化搜索,可以不加。
点击结果旁边的链接图标打开链接地址,如果出现: Ollama is running 说明可用。在可用地址后面拼上 /api/tags 查看部署的模型。
得到的 http://xxx.xxx.xxx.xxx:11434/ 就是可用地址。
fofa.info 支持导出,导出一份结果直接扫描一遍:
扫描程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response;
import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*;
public class OllamaChecker {
private static final OkHttpClient httpClient = new OkHttpClient.Builder() .connectTimeout(2, TimeUnit.SECONDS) .readTimeout(2, TimeUnit.SECONDS) .writeTimeout(2, TimeUnit.SECONDS) .build();
private static final String INPUT_FILE = "/Users/xxx/202502161353.csv"; private static final String OUTPUT_FILE = "/Users/xxx/202502161353.txt";
public static void main(String[] args) throws IOException, InterruptedException { List<String> urls = readUrlsFromCsv(INPUT_FILE);
ExecutorService executorService = Executors.newFixedThreadPool(16); List<Future<Result>> futures = new ArrayList<>();
for (String url : urls) { futures.add(executorService.submit(new UrlProcessor(url))); }
List<String> results = new ArrayList<>(); int count = futures.size(); int index = 0; for (Future<Result> future : futures) { index++; try { Result result = future.get(); if (result.models != null && !result.models.isEmpty()) { results.add(result.url + " " + result.models); System.out.println("[" + index + "/" + count + "]check url: " + result.url + ", models: " + result.models); } } catch (ExecutionException e) { e.printStackTrace(); } }
executorService.shutdown(); executorService.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("check url done. save result..."); writeResultsToFile(results, OUTPUT_FILE); System.out.println("save result done. exit."); }
private static List<String> readUrlsFromCsv(String fileName) throws IOException { List<String> urls = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { String line; while ((line = reader.readLine()) != null) { String[] columns = line.split(","); if (columns.length >= 6) { String link = columns[5].trim(); if (link.startsWith("http")) { urls.add(link); }
} } } return urls; }
private static void writeResultsToFile(List<String> results, String fileName) throws IOException { try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) { for (String result : results) { writer.write(result); writer.newLine(); } } }
static class UrlProcessor implements Callable<Result> { private final String url;
public UrlProcessor(String url) { this.url = url; }
@Override public Result call() throws Exception { String fullUrl = url + "/api/tags"; try { String jsonResponse = sendHttpRequest(fullUrl);
JSONObject jsonObject = JSON.parseObject(jsonResponse); JSONArray modelsArray = jsonObject.getJSONArray("models");
if (modelsArray != null && !modelsArray.isEmpty()) { StringBuilder modelsBuilder = new StringBuilder(); for (int i = 0; i < modelsArray.size(); i++) { JSONObject model = modelsArray.getJSONObject(i); if (i > 0) { modelsBuilder.append(","); } modelsBuilder.append(model.getString("model")); } return new Result(url, modelsBuilder.toString()); } } catch (Exception e) { System.out.println("invalid url: " + fullUrl); } return new Result(url, null); }
private String sendHttpRequest(String url) throws IOException { Request request = new Request.Builder().url(url).build(); try (Response response = httpClient.newCall(request).execute()) { if (response.isSuccessful() && response.body() != null) { return response.body().string(); } throw new IOException("Unexpected code " + response); } } }
static class Result { String url; String models;
public Result(String url, String models) { this.url = url; this.models = models; } }
}
|
FOFA
FOFA 是一个网络空间搜索引擎,可以帮助用户快速发现和定位互联网上的资产。它的查询语法类似于常见的搜索引擎语法,但针对网络设备、服务、漏洞等信息进行了优化。以下是 FOFA 查询语法的一些基本用法和示例:
1. 基本语法
FOFA 的查询语法主要基于键值对的形式,支持多种字段的组合查询。
常见字段
host: 搜索特定主机或域名。
ip: 搜索特定 IP 地址或 IP 段。
port: 搜索特定端口。
protocol: 搜索协议类型(如 HTTP、HTTPS、FTP 等)。
banner: 搜索服务返回的 Banner 信息。
title: 搜索网页标题。
header: 搜索 HTTP 响应头内容。
body: 搜索网页正文内容。
cert: 搜索 SSL/TLS 证书信息。
country: 搜索国家(使用国家代码,如 CN 表示中国)。
region: 搜索地区。
city: 搜索城市。
org: 搜索组织名称。
server: 搜索服务器类型(如 Apache、Nginx 等)。
icp: 搜索 ICP 备案号。
2. 查询语法示例
单字段查询
解释:搜索网页标题中包含“登录页面”的结果。
解释:搜索指定 IP 地址的结果。
解释:搜索开放了 8080 端口的设备。
多字段组合查询
可以使用 &&(与)、||(或)、!=(不等于)等逻辑运算符进行组合查询。
1
| ip="192.168.1.1" && port="80"
|
解释:搜索 IP 地址为 192.168.1.1 且开放了 80 端口的设备。
1
| title="管理后台" || title="Admin Panel"
|
解释:搜索网页标题中包含“管理后台”或“Admin Panel”的结果。
1
| country="CN" && protocol="https"
|
解释:搜索位于中国且使用 HTTPS 协议的服务。
模糊匹配
可以使用通配符或正则表达式进行模糊匹配。
解释:搜索网页标题中包含“登录”关键字的结果。
解释:搜索网页正文中包含“password”的结果。
1
| banner="/Apache.*Server/"
|
解释:搜索 Banner 中包含“Apache”后跟任意字符再接“Server”的结果。
排除条件
使用 != 或 - 排除某些条件。
1
| ip="192.168.1.1" && port!="22"
|
解释:搜索 IP 地址为 192.168.1.1 但不开放 22 端口的设备。
解释:搜索位于中国但不在北京的设备。
IP 段查询
支持 CIDR 表示法和范围表示法。
解释:搜索 192.168.1.0 到 192.168.1.255 范围内的所有 IP。
1
| ip="192.168.1.1-192.168.1.100"
|
解释:搜索从 192.168.1.1 到 192.168.1.100 的 IP 地址。
高级查询
结合多个字段和逻辑运算符实现复杂查询。
1
| (country="US" || country="CN") && (port="443" || port="80") && server="nginx"
|
解释:搜索位于美国或中国,开放了 443 或 80 端口,并且使用 Nginx 作为服务器的结果。
3. 注意事项
- 字段区分大小写:部分字段(如
title、body)可能对大小写敏感。
- 特殊字符转义:如果查询中包含特殊字符(如双引号),需要使用反斜杠
\ 进行转义。
- 结果数量限制:免费用户可能有查询结果数量的限制,付费用户可以解锁更多功能。
- 合法合规:使用 FOFA 时,请确保遵守相关法律法规,避免非法用途。
通过以上语法,您可以灵活地利用 FOFA 进行网络资产的搜索和分析。如果您有更具体的需求或问题,欢迎进一步提问!