Crossintheroof
博客园有毒,这题一放出来就加载不了,换个地方看吧:
http://phoebe233.cn/index.php/archives/32/#toc-Crossintheroof
shithappened
考点:flask+haproxy解析cookie
题解
这题是haproxy+flask,起初一直以为是这个haproxy的cve,一直没找到利用,唉,现在只有一个视频的wp:
https://www.youtube.com/watch?v=vayPeD_YB1g
首页是一个表单,点击会提交到/admin页面,但是会显示403,并且给出了haproxy的配置:
global
daemon
log 127.0.0.1 local0 debug
defaults
log global
retries 3
maxconn 2000
timeout connect 5s
timeout client 50s
timeout server 50s
resolvers docker_resolver
nameserver dns 127.0.0.11:53
frontend internal_access
bind 127.0.0.1:8080
mode http
use_backend test
frontend internet_access
bind *:80
errorfile 403 /etc/haproxy/errorfiles/403custom.http
http-response set-header Server Server
http-request deny if METH_POST
http-request deny if { path_beg /admin }
http-request deny if { cook(IMPERSONATE) -m found }
http-request deny if { hdr_len(Cookie) gt 69 }
mode http
use_backend test
backend test
balance roundrobin
mode http
server flaskapp app:8282 resolvers docker_resolver resolve-prefer ipv4
最重要的是这四个deny:
http-request deny if METH_POST
http-request deny if { path_beg /admin }
http-request deny if { cook(IMPERSONATE) -m found }
http-request deny if { hdr_len(Cookie) gt 69 }
分别为:
拒绝POST请求
路径不能为/admin
Cookie中不能有IMPERSONATE
Cookie长度<69
最终payload为:
GET //admin HTTP/1.1
...
Cookie: KEY=0be40039bcd8286eab237f481641b16e5e3ab442e0bc1135f08c143b22dc1efc;
Cookie: ;=IMPERSONATE=admin
用HEAD请求替代POST,//admin等同于/admin,然后可以看到提示我们设置Cookie
由于HTTP1.1支持cookie分行传输,所以可以以下方法绕过长度69限制
Cookie: KEY=0be40039bcd8286eab237f481641b16e5e3ab442e0bc1135f08c143b22dc1efc;
Cookie: IMPERSONATE=admin
这样已经绕过了1、2、4,但是如果直接这样还是会被禁
HEAD //admin HTTP/1.1
...
Cookie: KEY=0be40039bcd8286eab237f481641b16e5e3ab442e0bc1135f08c143b22dc1efc;
Cookie: IMPERSONATE=admin
(原因在最后)
先来看看文档里的cook():
文档:https://cbonte.github.io/haproxy-dconv/1.7/configuration.html
然后wp用了
;=IMPERSONATE=admin
的方法进行绕过,个人理解是由于文档中所说:
这将提取“ Cookie”上最后一次出现的Cookie名称<name>
首先
Cookie: KEY=0be40039bcd8286eab237f481641b16e5e3ab442e0bc1135f08c143b22dc1efc;
Cookie: ;=IMPERSONATE=admin
cook()取到的是我们最后一次出现的cookie,也就是;=IMPERSONATE
,而不是IMPERSONATE
,那么此时haproxy拿到的cookie为:{";":"IMPERSONATE=admin"}
而flask是不支持;
或=
作为cookie名的,所以flask的特性会忽略;
自动往后取到IMPERSONATE
那么经过cook()+flask解析就变成{"IMPERSONATE":"admin"}
成功bypass了,然后将HEAD改成get就看到flag了,如下:
以上的图来自于:https://www.youtube.com/watch?v=vayPeD_YB1g
这里要感谢这个友善的外国小哥:
本地测试一下,如下是一个简单的flask,观察到;
或=
都不影响flask的cookie设置
下面测试分行传输cookie,pass成功被赋值1
一点小思考:
cookie中如果有相同的键是会被后者覆盖的,如下pass=2覆盖pass=1:
但是为什么分行cookie的pass=3没有覆盖pass=2,而加上;
就能被覆盖?
然后我用php测试结果如下:发现第二行的pass被加上了,_
而如果我们改成下面这样(注意分号)结果就为:{"pass":"123, "}
那么很显然,两行cookie直接使用,_
连接在一起,解释一下,当第二行前面被加上;
时,结果就变成这样:
cookie:pass=123;, ;pass=456;
转换成json格式为:
{"pass":123,",_":"","pass":456}
分割一下
"pass":123
",_":""
"pass":456
由于cookie键名中是不能有空格的,所以空格就被替换为_
便有了上面的结果
可以在键中间加一个空格,可以看到被解析成了_
回到flask,没加分号时:
cookie:pass=1;
cookie:pass=2;
实际的cookie就为:
cookie:pass=1;, pass=2;
json格式为:
{"pass"=1,",_pass"=2}
这样肯定不会被覆盖了,加了分号就把,_
给隔开了:
{"pass"=1,",_":"","pass"=2}
到此flask的多行cookie覆盖问题就说通了,也说明了为什么题目中不能直接这样分行传cookie,就是,_
的干扰!
Cookie: KEY=0be40039bcd8286eab237f481641b16e5e3ab442e0bc1135f08c143b22dc1efc;
Cookie: IMPERSONATE=admin
但令我疑惑的是为什么php中不支持cookie覆盖?(占坑)