极客大挑战的”服务端检测系统”
这道题没做出来,看来writeup之后发现是因为自己基础知识不足。这里补一下。
关于HTTP头的一些小知识
http头的格式:
1 | 请求方法 /路径 HTTP/版本 |
其中:
http头部的各个字段之间用
CRLF(\r\n 或 %0d%0a)
分割
http请求头(响应头)和请求体(响应体)之间用两个CRLF
分割
最简单的Http请求
get请求:
1 | GET / |
post请求
1 | POST / |
http头的字段(一小部分)
Host
用于多个域名映射到同一个ip时,按照Host的内容匹配不同网站内容,每个Http头必须有Host头
当这个目的主机上只有一个网站,那么Host字段内容可以为任意值
Content-Type
表明内容部分的编码格式和媒体类型 post/put等方式必须有(如果有请求体)
请求头中的Content-Type
GET请求:
GET没有请求实体部分,所以请求头中不需要设置Content-Type字段
POST/PUT请求
如果post/put请求有请求体(有传值),那么必须要设置Content-Type字段(否则服务端可能无法处理你的请求)
- 第一类:raw原始类型,可以上传任意格式的文本,比如text、json、xml、html..(中文不进行编码)
text请求:Content-Type: text/plain
json请求:Content-Type: application/json
html请求:Content-Type: text/html - 第二类:application/x-www-from-urlencoded,会将表单内的数据转换拼接成key-value对(对非ascii码进行编码)
Content-Type: application/x-www-from-urlencoded - 第三类:multipart/from-data,将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。
Content-Type: multipart/from-data;boundary=xxx
在请求体中会有一个Content-Disposition字段,用于描述提交的表单的具体信息
响应头中的Content-Type:
用来告诉客户端,服务端返回的真实内容类型是什么。浏览器或客户端会根据这个值来做一些操作。
Content-Length 表示内容部分的长度 post/put等方式必须有(如果有请求体)
了解了上面的东西再来看这道题
题目给了源码,可控的变量是url和method,提示了admin.php,但要本地才能访问。
1 | <!-- /admin.php --> |
注意到这里将method放在了%d的前面,如果method为%s%
,name结尾的%就会将%d中的%给转移掉,而前面的%s就可以将body中的内容给打印出来。
而method又会作为http请求的方式,但不影响,因为某些中间件会将不认识的方式默认当做GET请求来响应
1 | echo "failed<br>"; |
提交method=%s%,得到:
代码为:
1 |
|
这里要post一个iwantflag=yes才能获得flag
再看看下面这串代码
1 | $opts = array( |
这里使用的stream_context_create和file_get_contents来模拟http请求,method是直接拼接的我们的输入,所以这里我们可以构造一个任意的http请求。
举个列子:
这里服务器的代码为
1 |
|
我们用nc来监听本机的8808端口,看看file_get_contents发送的http头数据
1 | nc -lp 8808 |
如果method为正常的GET,这时候就是正常的一个http请求
而也很容易想到,我们可以通过method来进行注入,使http头变成我们想要的样子,比如我输入
1 | method=POST / HTTP/1.1 |
这时发出的http请求就会变成
可以看到,前面的包已经完全成为我们可控的了。
到这里已经很明白了,只要通过method构造我们的http请求头即可
最后payload:
1 | url=http://127.0.0.1&method=POST /admin.php HTTP/1.1 |
最后加一个参数是为了防止后面的东西影响到前面的iwantflag参数
%26是&的url编码格式,防止在提前被认为是参数分隔符。
最后,得到flag