Midnight sun CTF 2020

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覆盖?(占坑)
在这里插入图片描述

posted @ 2020-04-11 12:25  W4nder  阅读(256)  评论(0编辑  收藏  举报