goahead学习
0x00 http协议
http/0.9
- http是基于tcp/ip协议的应用层协议,不涉及数据包传输,主要规定了客户端和服务器之间的通信格式。
- 0.9版本只有一个命令GET
1 | GET /index.html |
1 | <html> |
- 协议规定,服务器只可以回应html格式的字符串,等服务器发送完毕,就关闭TCP连接。
http/1.0
- 加入了POST和HEAD命令,可以传输图像、视频、二进制文件。
- http的请求和回应的格式变了,除了之前的html部分,每次通信需要包括头信息,用来描述一些元数据(用来描述数据的数据)。
- 还新增状态码,多字符集支持,权限等等。
1 | GET / |
1 | 200 OK |
回应的格式是”头信息 + 一个空行(
\r\n
) + 数据”Content-Type字段的作用是告诉客户端,将要传回的数据是什么格式的。
这些数据类型总称为MIME type
http1.0只能发送一个请求,发送数据完毕,连接就会关闭。如果还要请求其他的资源,就必须新建一个连接。为了解决这个问题,有些浏览器会在请求的时候,使用
1 | Connection: keep-alive |
这个字段要求服务器不关闭连接,方便请求复用,服务器会同样回应这个字段。只是这种方法不是标准字段,所以会有不同实现,所以不是解决问题的方法。
http/1.1
- http1.1最大的变化是引入了持久连接,即tcp连接默认不关闭,而且不需要声明
Connection: keep-alive
,当客户端和服务器发现对方有一段时间没有活动,就会主动关闭连接。更规范的做法是客户端在最后一个请求时,发送Connection: close
以此要求服务器关闭。 - 1.1版本引入了管道机制,以前在同一个tcp连接中,发送了A请求,然后等待服务器作出回应,收到后再发出B请求。管道机制可以使浏览器同时发出AB请求,但是仍然按照顺序完成请求。
- 为了分清tcp连接的字段,使用
Content-Length
声明数据长度。在浏览器接收了声明的长度后,后面的字段就属于下一个回应了。在之前的版本中这个字段不是必须的,因为服务器关闭tcp连接,就表明数据包已经全了。 Transfer-Encoding: chunked
表示数据包由数量未定的数据块组成,在每个数据块之前会有一个16进制的数据,表示这个块的大小,最后一个是大小为0的块,表示数据发送完成。- 1.1增加了PUT、PATCH、HEAD、OPTIONS、DELETE。
- 增加了host字段
http/2.0
- 2.0实现了在同一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不需要按照顺序。
- head信息可以压缩后再发送
0x01 web路由
- 路由就是url到函数的映射,它用来跟后端服务器进行交互,通过不同的路径去请求不同的资源,请求不同的页面是路由的其中一项功能。
- route是一条路由,它将一个URL路径和一个函数进行映射
1 | /users -> getAllUsers() |
- router可以理解为一个容器,它管理了一组route,在接收到一个url之后,会去路由映射表中查找相应的函数,这个过程由router处理。
0x02 goahead
goadhead是一个嵌入式web服务器,在它的官方文档当中详细的阐述了route.txt定义的路由规则。
根据匹配的url来执行不同的handler:有action handler直接在goahead进程中执行c函数,cgi handler执行新的cgi程序,file handler处理文件请求,也可以自定义新的handler。开发者自己定义的goaction就是常见的审计点。
iot固件中常见的情况是使用2.18版本的goahead。
main.c@main
1 | if (initWebs() < 0) { |
main.c@initwebs
因为goahead的项目时间比较久了,所以在它中间找到漏洞比较困难,我们直接去看它的路由解析,转到不同的handler函数的审计。
1 | /* |
websUrlHandlerDefine会在读取完http请求头后,根据url前缀来执行相应的handler函数。
1 | int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg, |
代码都比较简单,就是进行路由初始化,就不多介绍了,我们使用vscode的shift+f12可以找到在哪里还引用了该路由数组。
gstrncmp函数会将请求的path和路由对象的urlPrefix字符串进行匹配,对于空字符串一定会匹配成功。
所以它自己定义的第一条和第四条是一定会被匹配到的。
然后handler返回1直接结束路由分发,返回0继续匹配其他路由。
我们继续寻找websUrlHandlerRequest的引用,会找到webs.c@websReadEvent,这个函数会for循环处理用户请求数据。websUrlHandlerRequest会设置好环境变量,然后websReadEvent就会处理。
asp.c@websAspDefine
main.c@initWebs函数内部会调用asp.c@websAspDefine定义的一些asp函数,这些函数是给asp文件调用的。
asp通过<%....;%>
的格式去调用上面定义的asp函数
漏洞点
- 定位websSecurityHandler函数,可以分析登录验证的逻辑
- 定位websDefaultHandler函数,可以分析asp文件请求
- 自定义的handler
0x03 cgi
1 | http://hacker.com/cgi-bin/test.cgi |
在web服务器调用test.cgi之前,会把http请求中的信息以环境变量的形式写入os。cgi可以通过本身函数获取环境变量,从而获得数据输入。除了环境变量,cgi程序还可以通过标准输入获得。比如post请求一个cgi的url,那么post的数据,cgi可以通过标准输入(stdin)得到。
cgi构造数据(比如html页面)时,只要按照标准输出的方式就可以,因为web服务器已经做好了重定向,将标准输出重定向给web服务器与浏览器的socket。
cgi程序本质上是os上一个可执行程序,作为http服务器的时候,客户端可以通过get或者post请求调用这段可执行程序。因为html是静态页面,所以没办法实现一些复杂的功能,但是cgi可以。
0x04 分析思路
首先在分析GoAhead的固件之前,要先知道该框架的版本,这样除了能够知道历史漏洞之外,还能对着该版本源码进行分析,快速定位到关键点。可以直接在ida里搜字符串即可得知。
goahead支持类似asp的服务器端脚本语言,该版本和微软版本语法基本相同,使用asp可以高效开发动态页面。