Gopher-HTTP2

前言

最近逛pwnhub的时候,看到了以前的一道题Pink friend,因为题目已经关闭的原因,所以我直接去看了下题解,发现题目的解法是利用gopher来发HTTP2的包,顿时觉得很有意思,这种操作方式和HTTP2都是我所不熟悉的,所以决定要稍微研究一下,然后写篇文章总结一下。本文不侧重HTTP2包的分析,网上有很多的分析文章,本文只是研究总结下这种操作方式。


Nginx配置HTTP2

Nginx配置很简单,只要在sites-available的HTTPS配置文件中加上http2即可:

1
2
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;

因为操作环境的需要,所以我也给HTTP的配置文件开启了http2支持。之后重启Nginx即可看到它支持了HTTP2:

配置抓包HTTPS

因为浏览器只支持建立在HTTPS上的HTTP2的缘故,如果用浏览器访问建立在HTTP上的HTTP2,就会变成下载一个服务端发来的HTTP2的升级要求包了,十六进制如下:

1
000012040000000000000300000080000400010000000500FFFFFF0000040800000000007FFF00000000080700000000000000000000000001

所以为了更好地研究HTTP2的一些交互流程,我们就需要从HTTPS来进行抓包了。

HTTPS的抓包流程详情见参考文章1,主要的几步就是:

  • 关闭浏览器
  • 配置环境变量SSLKEYLOGFILE为一个log文件,这样浏览器就会把公钥储存在这里
  • 配置wireshark的首选项/Protocols/ssl/(Pre)-Master-Secret log filename为该log文件
  • 启动抓包和浏览器

抓包结果:

这个抓包流程其实有点坑,有时候抓到的包会无法解密,如果遇到这种情况就可以尝试从第一步重新来过了……

不想麻烦的话,也可以利用curl+nc的方式来获取HTTP2的请求包,详情见参考文章4。

发送请求

后一半的包是属于第二个HTTP2请求的,我们主要看前一半的包。从抓到的包可以看到,我们只需要发送两个包就可以了。我们模拟客户端有两种方式,一种是使用curl+gopher,另一种是使用pwntools,我们先把数据取出来(追踪SSL流,然后复制原始数据),然后加工成能够使用的样子:

然后将服务端的返回包保存起来,等下一包进行解包查看数据。curl也是差不多的处理方式,只要将请求包URL编码一下就好了:

1
curl -vv gopher://laohulaohuhu.cn:80/_PRI%20%2A%20HTTP/2.0%0D%0A%0D%0ASM%0D%0A%0D%0A%00%00%12%04%00%00%00%00%00%00%01%00%01%00%00%00%03%00%00%03%E8%00%04%00%60%00%00%00%00%04%08%00%00%00%00%00%00%EF%00%01%00%01%06%01%25%00%00%00%01%80%00%00%00m%82A%8B%A0g%9E%DA%06y%ED%9E%D5%C9W%87%84%40%85%AE%DB%2B%3A%0B%86%AE%C2%CAT%92%7F%40%92%B6%B9%AC%1C%85X%D5%20%A4%B6%C2%ADa%7BZT%25%1F%011z%D4%D0%7Ff%A2%81%B0%DA%E0S%FA%E4j%A4%3F%84%29%A7z%81%02%E0%FBS%96%AEN5%FD%A9%0Du%D0%5EE%8F1%92%C3l%BA%BB.%29%FDf%C7%BFF%7F%A5%287R%A9%88%A4%EA%7F%EDK%D3%D8zJ%C3%AC%AE%05%D9q%E6Wy%C57%0EQ%D8f%1Be%D5%D9sS%D8I%7C%A5%89%D3M%1FC%AE%BA%0CA%A4%C7%A9%8F3%A6%9A%3F%DF%9Ah%FA%1Du%D0b%0D%26%3DLy%A6%8F%BE%D0%01w%FE%8DH%E6%2B%1E%0B%1D%7FF%A4s%15%81%D7T%DF_%2C%7C%FD%F6%80%0B%BD%F4%3A%EB%A0%C4%1ALz%98A%A6%A8%B2%2C_%24%9CuL_%BE%F0F%CFP%8D%9B%D9%AB%FARB%CB%40%D2_%A5%23%B3Q%8C%F7%3A%D7%B4%FD%7B%9F%EF%B4%00%5D%FF --output data

解包

解包可以使用writeup中的方式,即伪造一个HTTP2服务器,将保存的返回包发送给CURL,我们就可以看到解包的结果了,伪造服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
import socket

a = open("data", "rb").read()
s = socket.socket()
host = '127.0.0.1'
port = 2333
s.bind((host, port))

s.listen(5)
while True:
c, addr = s.accept()
print 'Connect from:', addr
c.send(a)

curl访问:

1
curl --http2-prior-knowledge -v http://127.0.0.1:2333 --output -

结果如下:

可以看到服务端的响应头和gzip编码后的响应主体,我们直接gzip解压,即可看到HTML:

1
2
3
4
5
6
7
8
9
root@05b1494e5951:~# mv data2 data2.gz
root@05b1494e5951:~# gzip -d data2.gz
root@05b1494e5951:~# ls
curl data data2 s.py
root@05b1494e5951:~# cat data2
<html>
<meta http-equiv="refresh" content="0;url=https://*.*.*.*/">
</html>
root@05b1494e5951:~#

参考文章:

https://blog.csdn.net/vetala/article/details/79172605

https://www.jianshu.com/p/e57ca4fec26f

https://imququ.com/post/protocol-negotiation-in-http2.html

http://f1sh.site/2019/01/30/pwnhub-pink-friend-writeup/

http://www.pdosgk.com/Home/News/show/id/464798.html

https://altman.vip/2019/01/31/pwnhub-pinkfriend/