说在前面
因为赛制改革,这个月的月赛又是我在的队出的,除了我出的题目之外,做了另外一道web
题,感觉又涨见识了。
虽然不算完完全全靠自己做出来的,但也还算是拿到了flag
了吧。窃喜.jpg
WriteUp
打开这个题目…
网站已经有了一个title
为hello
的article
,内容为content
,可以通过http://xxx/article?title=hello
这样的url
来查询;
另外,返回包的内容显示,这个web
服务是用python
写的…(⊙o⊙)这是我头一次做到python
起的题目哎…不知道后台是怎么写的。
再没有其他信息了,第一反应是sql
注入,所以尝试了单引号、双引号,以及还瞎尝试了各种其他的特殊字符,都没用,什么都不返回。
感觉题目应该有两种可能:
title
参数这里存在注入;- 有一个让我添加新的
article
的地方。
title
这里已经尝试了,感觉没啥注入的可能性,那就找找有没有添加新的article
的地方,比如publish post
这样的链接…简单手动试了一下发现没有,就用burp
跑字典。当时跑字典的时候其实是跑出来/add
这个目录的,可惜…我没注意,提示是方法不允许,我第一反应就是http
的请求方法不允许add
。。。到底是怎样的脑回路让我做到…这样想的,http
的请求方法里根本没有add
好嘛??
然后到这里我就卡死了…即没有注入,又没有添加的地方,那还怎么搞???
然后我就邪恶地ssh
连接了服务器,看了这个题目的源码…这就是场外力量??
(所以接下来到底应该以什么样的角度来说writeup
呢??)
看了源码之后觉得这个题目缺少提示,尤其是在访问/add
的时候,是需要验证session.get("is_admin")
是否存在的,这不提示的话,做题人怎么知道是验证的is_admin
呢???
要想访问/add
需要满足两个条件:请求方法是post
,不能是其他;要有is_admin
这个session
值。第一个条件容易满足,然后就去查python
要怎么改session
值。
这个web
服务是用了python flask
,主要参考了这篇
https://www.jianshu.com/p/faad255f587a
按照这个帖子最后的那段代码,伪造session
…就可以绕过第二个条件的检测啦。这里还要提到那个secret_key
…这个key
值是保存在secret_key.py
文件中的,这个文件名应该是要猜出来的…(目前还不知道正确做法要怎么拿到这个文件,如果不是猜出来的话,之后会更新一下)通过http://xxx/article?title=../secret_key.py
来读取到。
2019.02.25更新
- 拿到
secret_key
是通过直接读取secret_key.py
文件来获取的; - 这个题目预期就是要让大家拿到源码,拿到最关键的
s3rver.py
是通过读取/proc/self/cmdline
,在里面会看到启动方式,python /var/www/web_xxxx/s3rver.py
,然后再利用路径穿越,任意文件读取拿到源码…可不是通过看服务器后台哦…/坏笑.gif
emm…刚开始就应该想到任意文件读的…我是到做到后面时候才突然反应过来这么个点可以利用的…
所以整个做题过程就是:任意文件读拿到源码,在源码里看到secret_key.py flag.py
,再去读secret_key.py
拿到key
值,伪造session
,进入到/add
页面,再利用SSTI
,进行任意代码执行,拿到flag
说到这里,我要歪一下楼,这个题目不是用数据库来存储信息的,article
内容是存储在文件夹下的,通过读取文件来将查询结果显示到文件中。我感觉如果我没有看源码的话,我可能想不到是要读文件,也不会猜到要通过title
去读文件,拿到key
的值。title
这个参数在后台只检测了flag
字符串,所以存在路径穿越问题,但是有这个问题还是不足以拿到flag
。(做的时候利用路径穿越问题查看了.bash_history
文件的内容,不过没啥用)
好了,接着往下说。
把伪造session
的代码贴一下。跟上面那个参考链接里的一样。
from flask import Flask, session
app = Flask(__name__)
app.config['SECRET_KEY']='ccedfbd953b3f4692e053822a4e58cac'
@app.route('/flag')
def set_id():
session['is_admin']=1
return "ok"
#app.run(debug=True)
这就进来/add
目录了,接下来题目的逻辑就是post
一个title
和content
值,后台会创建对应的文件,并将content
内容写入到文件中,而且在写入成功之后会再读取文件显示到网页上。
因为在article?title=
这里是限制了不能输入flag
的,所以肯定还是要从add
这里下手,怎么能读出flag
。我刚开始一直在想能不能光靠路径穿越做出题目…就是说这个题目到底有没有非预期解,后来想想,如果要从article?title=
这里下手,不管怎么样,在后台都是通过open()
函数读取flag
文件内容来拿到flag
的,那我就一定要输入flag
这个字符串(刚开始试了一下open
函数有没有通配符这种用法,发现没有…),那就只能从add
这里想办法了。
emm…然后去吃了个饭,回来也不知道是咋搜的,搜到了这篇文章。
https://www.jianshu.com/p/6e4aebd18660
原来这是一个常见的攻击啊。没用过flask
不知道,能查到也不错,嘿嘿。直接把payload
换上去。
title=hi&content={{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].popen('grep -rn "flag" /var/www').read()}} //拿到flag文件的路径
title=hi&content={{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].popen('cat /var/www/web_sertdf/flag.py').read()}}
完美~ 可以执行任意指令,拿到flag
就轻轻松松啦~~
把主要的代码贴一下:
这里还有个问题…感觉flag.py
应该设置一下写权限,要不然就可能出现搅屎的情况了。??在写title
时候写为title=../flag.py
,然后flag
文件就被覆盖,别的队都看不到了。??
总结
这可能是第一次一个题目磨蹭了一天吧,虽然中间也间隔着做了一些其他事情,比如睡个午觉…遛个圈。还是感觉方法论很重要,要在做题过程中指导自己,可能有哪几种方法,可以排除哪些方法,为什么排除这样…
这个题目也是自己没见过,不熟悉的东西,然后能找到相关帖子,找到解题方法,哇,突然感觉自己进步好大…
不过话说回来,不进到后台的话…我可能只有和出题人py
一下才能做出来了…hhhhh…我到底在这里说什么方法论??哈??